From 651d8ff58347abf531f1ebb4d75abfae5c00ca0b Mon Sep 17 00:00:00 2001 From: Olivier Poitrey Date: Tue, 6 Nov 2012 10:31:42 +0100 Subject: [PATCH] Serialize all background operations --- SDWebImage/SDImageCache.m | 44 ++++++++-------------- SDWebImage/SDWebImageDownloader.m | 12 +++++- SDWebImage/SDWebImageDownloaderOperation.h | 1 + SDWebImage/SDWebImageDownloaderOperation.m | 15 +++++--- 4 files changed, 37 insertions(+), 35 deletions(-) diff --git a/SDWebImage/SDImageCache.m b/SDWebImage/SDImageCache.m index f3a8c3b2..f4b13868 100644 --- a/SDWebImage/SDImageCache.m +++ b/SDWebImage/SDImageCache.m @@ -19,6 +19,7 @@ static const NSInteger kDefaultCacheMaxCacheAge = 60 * 60 * 24 * 7; // 1 week @property (strong, nonatomic) NSCache *memCache; @property (strong, nonatomic) NSString *diskCachePath; +@property (assign, nonatomic) dispatch_queue_t ioQueue; @end @@ -44,6 +45,9 @@ static const NSInteger kDefaultCacheMaxCacheAge = 60 * 60 * 24 * 7; // 1 week { NSString *fullNamespace = [@"com.hackemist.SDWebImageCache." stringByAppendingString:ns]; + // Create IO serial queue + _ioQueue = dispatch_queue_create("com.hackemist.SDWebImageCache", DISPATCH_QUEUE_SERIAL); + // Init default values _maxCacheAge = kDefaultCacheMaxCacheAge; @@ -87,6 +91,7 @@ static const NSInteger kDefaultCacheMaxCacheAge = 60 * 60 * 24 * 7; // 1 week - (void)dealloc { [[NSNotificationCenter defaultCenter] removeObserver:self]; + dispatch_release(_ioQueue); } #pragma mark SDImageCache (private) @@ -115,7 +120,7 @@ static const NSInteger kDefaultCacheMaxCacheAge = 60 * 60 * 24 * 7; // 1 week if (toDisk) { - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^ + dispatch_async(self.ioQueue, ^ { NSData *data = imageData; @@ -175,37 +180,18 @@ static const NSInteger kDefaultCacheMaxCacheAge = 60 * 60 * 24 * 7; // 1 week return; } - NSString *path = [self cachePathForKey:key]; - dispatch_io_t ioChannel = dispatch_io_create_with_path(DISPATCH_IO_STREAM, path.UTF8String, O_RDONLY, 0, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), nil); - dispatch_io_read(ioChannel, 0, SIZE_MAX, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^(bool done, dispatch_data_t dispatchedData, int error) + dispatch_async(self.ioQueue, ^ { - if (error) + UIImage *diskImage = [UIImage decodedImageWithImage:SDScaledImageForPath(key, [NSData dataWithContentsOfFile:[self cachePathForKey:key]])]; + + if (diskImage) { - if (error != 2) - { - NSLog(@"SDWebImageCache: Error reading image from disk cache: errno=%d", error); - } - doneBlock(nil); - return; + [self.memCache setObject:diskImage forKey:key cost:image.size.height * image.size.width * image.scale]; } - dispatch_data_apply(dispatchedData, (dispatch_data_applier_t)^(dispatch_data_t region, size_t offset, const void *buffer, size_t size) - { - UIImage *diskImage = [UIImage decodedImageWithImage:SDScaledImageForPath(key, [NSData dataWithBytes:buffer length:size])]; + doneBlock(diskImage); - if (diskImage) - { - [self.memCache setObject:diskImage forKey:key cost:image.size.height * image.size.width * image.scale]; - } - - doneBlock(diskImage); - - return true; - }); }); - - dispatch_release(ioChannel); - } - (void)removeImageForKey:(NSString *)key @@ -224,7 +210,7 @@ static const NSInteger kDefaultCacheMaxCacheAge = 60 * 60 * 24 * 7; // 1 week if (fromDisk) { - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^ + dispatch_async(self.ioQueue, ^ { [[NSFileManager defaultManager] removeItemAtPath:[self cachePathForKey:key] error:nil]; }); @@ -238,7 +224,7 @@ static const NSInteger kDefaultCacheMaxCacheAge = 60 * 60 * 24 * 7; // 1 week - (void)clearDisk { - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^ + dispatch_async(self.ioQueue, ^ { [[NSFileManager defaultManager] removeItemAtPath:self.diskCachePath error:nil]; [[NSFileManager defaultManager] createDirectoryAtPath:self.diskCachePath @@ -250,7 +236,7 @@ static const NSInteger kDefaultCacheMaxCacheAge = 60 * 60 * 24 * 7; // 1 week - (void)cleanDisk { - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^ + dispatch_async(self.ioQueue, ^ { NSDate *expirationDate = [NSDate dateWithTimeIntervalSinceNow:-self.maxCacheAge]; NSDirectoryEnumerator *fileEnumerator = [[NSFileManager defaultManager] enumeratorAtPath:self.diskCachePath]; diff --git a/SDWebImage/SDWebImageDownloader.m b/SDWebImage/SDWebImageDownloader.m index 767d8852..b29e5b14 100644 --- a/SDWebImage/SDWebImageDownloader.m +++ b/SDWebImage/SDWebImageDownloader.m @@ -20,6 +20,8 @@ NSString *const kCompletedCallbackKey = @"completed"; @property (strong, nonatomic) NSOperationQueue *downloadQueue; @property (strong, nonatomic) NSMutableDictionary *URLCallbacks; +// This queue is used to serialize the handling of the network responses of all the download operation in a single queue +@property (assign, nonatomic) dispatch_queue_t workingQueue; @property (assign, nonatomic) dispatch_queue_t barrierQueue; @end @@ -66,11 +68,19 @@ NSString *const kCompletedCallbackKey = @"completed"; _downloadQueue = NSOperationQueue.new; _downloadQueue.maxConcurrentOperationCount = 10; _URLCallbacks = NSMutableDictionary.new; + _workingQueue = dispatch_queue_create("com.hackemist.SDWebImageDownloader", DISPATCH_QUEUE_SERIAL); _barrierQueue = dispatch_queue_create("com.hackemist.SDWebImageDownloaderBarrierQueue", DISPATCH_QUEUE_CONCURRENT); } return self; } +- (void)dealloc +{ + [self.downloadQueue cancelAllOperations]; + dispatch_release(_workingQueue); + dispatch_release(_barrierQueue); +} + - (void)setMaxConcurrentDownloads:(NSInteger)maxConcurrentDownloads { _downloadQueue.maxConcurrentOperationCount = maxConcurrentDownloads; @@ -93,7 +103,7 @@ NSString *const kCompletedCallbackKey = @"completed"; request.HTTPShouldHandleCookies = NO; request.HTTPShouldUsePipelining = YES; [request addValue:@"image/*" forHTTPHeaderField:@"Accept"]; - operation = [SDWebImageDownloaderOperation.alloc initWithRequest:request options:options progress:^(NSUInteger receivedSize, long long expectedSize) + operation = [SDWebImageDownloaderOperation.alloc initWithRequest:request queue:self.workingQueue options:options progress:^(NSUInteger receivedSize, long long expectedSize) { if (!wself) return; SDWebImageDownloader *sself = wself; diff --git a/SDWebImage/SDWebImageDownloaderOperation.h b/SDWebImage/SDWebImageDownloaderOperation.h index 154a4e5a..b7afd7f4 100644 --- a/SDWebImage/SDWebImageDownloaderOperation.h +++ b/SDWebImage/SDWebImageDownloaderOperation.h @@ -16,6 +16,7 @@ @property (assign, nonatomic, readonly) SDWebImageDownloaderOptions options; - (id)initWithRequest:(NSURLRequest *)request + queue:(dispatch_queue_t)queue options:(SDWebImageDownloaderOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageDownloaderCompletedBlock)completedBlock diff --git a/SDWebImage/SDWebImageDownloaderOperation.m b/SDWebImage/SDWebImageDownloaderOperation.m index 26c218ab..ba2b8b31 100644 --- a/SDWebImage/SDWebImageDownloaderOperation.m +++ b/SDWebImage/SDWebImageDownloaderOperation.m @@ -21,6 +21,7 @@ @property (assign, nonatomic) long long expectedSize; @property (strong, nonatomic) NSMutableData *imageData; @property (strong, nonatomic) NSURLConnection *connection; +@property (assign, nonatomic) dispatch_queue_t queue; @end @@ -29,10 +30,11 @@ size_t width, height; } -- (id)initWithRequest:(NSURLRequest *)request options:(SDWebImageDownloaderOptions)options progress:(void (^)(NSUInteger, long long))progressBlock completed:(void (^)(UIImage *, NSError *, BOOL))completedBlock cancelled:(void (^)())cancelBlock +- (id)initWithRequest:(NSURLRequest *)request queue:(dispatch_queue_t)queue options:(SDWebImageDownloaderOptions)options progress:(void (^)(NSUInteger, long long))progressBlock completed:(void (^)(UIImage *, NSError *, BOOL))completedBlock cancelled:(void (^)())cancelBlock { if ((self = [super init])) { + _queue = queue; _request = request; _options = options; _progressBlock = progressBlock; @@ -142,8 +144,11 @@ { if (![response respondsToSelector:@selector(statusCode)] || [((NSHTTPURLResponse *)response) statusCode] < 400) { - self.expectedSize = response.expectedContentLength > 0 ? (NSUInteger)response.expectedContentLength : 0; - self.imageData = [NSMutableData.alloc initWithCapacity:self.expectedSize]; + dispatch_async(self.queue, ^ + { + self.expectedSize = response.expectedContentLength > 0 ? (NSUInteger)response.expectedContentLength : 0; + self.imageData = [NSMutableData.alloc initWithCapacity:self.expectedSize]; + }); } else { @@ -162,7 +167,7 @@ - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^ + dispatch_async(self.queue, ^ { [self.imageData appendData:data]; @@ -236,7 +241,7 @@ { self.connection = nil; - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^ + dispatch_async(self.queue, ^ { [[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStopNotification object:nil];