diff --git a/SDImageCache.h b/SDImageCache.h index c533639..aab5e6e 100644 --- a/SDImageCache.h +++ b/SDImageCache.h @@ -7,12 +7,13 @@ */ #import +#import "SDImageCacheDelegate.h" @interface SDImageCache : NSObject { NSMutableDictionary *memCache, *storeDataQueue; NSString *diskCachePath; - NSOperationQueue *cacheInQueue; + NSOperationQueue *cacheInQueue, *cacheOutQueue; } + (SDImageCache *)sharedImageCache; @@ -21,6 +22,8 @@ - (void)storeImage:(UIImage *)image imageData:(NSData *)data forKey:(NSString *)key toDisk:(BOOL)toDisk; - (UIImage *)imageFromKey:(NSString *)key; - (UIImage *)imageFromKey:(NSString *)key fromDisk:(BOOL)fromDisk; +- (void)queryDiskCacheForKey:(NSString *)key delegate:(id )delegate userInfo:(NSDictionary *)info; + - (void)removeImageForKey:(NSString *)key; - (void)clearMemory; - (void)clearDisk; diff --git a/SDImageCache.m b/SDImageCache.m index 035f2eb..f92d472 100644 --- a/SDImageCache.m +++ b/SDImageCache.m @@ -39,7 +39,9 @@ static SDImageCache *instance; // Init the operation queue cacheInQueue = [[NSOperationQueue alloc] init]; - cacheInQueue.maxConcurrentOperationCount = 2; + cacheInQueue.maxConcurrentOperationCount = 1; + cacheOutQueue = [[NSOperationQueue alloc] init]; + cacheOutQueue.maxConcurrentOperationCount = 1; // Subscribe to app events [[NSNotificationCenter defaultCenter] addObserver:self @@ -134,6 +136,45 @@ static SDImageCache *instance; [fileManager release]; } +- (void)notifyDelegate:(NSDictionary *)arguments +{ + NSString *key = [arguments objectForKey:@"key"]; + id delegate = [arguments objectForKey:@"delegate"]; + NSDictionary *info = [arguments objectForKey:@"userInfo"]; + UIImage *image = [arguments objectForKey:@"image"]; + + if (image) + { + [memCache setObject:image forKey:key]; + + if ([delegate respondsToSelector:@selector(imageCache:didFindImage:forKey:userInfo:)]) + { + [delegate imageCache:self didFindImage:image forKey:key userInfo:info]; + } + } + else + { + if ([delegate respondsToSelector:@selector(imageCache:didNotFindImageForKey:userInfo:)]) + { + [delegate imageCache:self didNotFindImageForKey:key userInfo:info]; + } + } +} + +- (void)queryDiskCacheOperation:(NSDictionary *)arguments +{ + NSString *key = [arguments objectForKey:@"key"]; + NSMutableDictionary *mutableArguments = [[arguments mutableCopy] autorelease]; + + UIImage *image = [[[UIImage alloc] initWithContentsOfFile:[self cachePathForKey:key]] autorelease]; + if (image) + { + [mutableArguments setObject:image forKey:@"image"]; + } + + [self performSelectorOnMainThread:@selector(notifyDelegate:) withObject:mutableArguments waitUntilDone:NO]; +} + #pragma mark ImageCache - (void)storeImage:(UIImage *)image imageData:(NSData *)data forKey:(NSString *)key toDisk:(BOOL)toDisk @@ -154,7 +195,6 @@ static SDImageCache *instance; { [storeDataQueue setObject:data forKey:key]; [cacheInQueue addOperation:[[[NSInvocationOperation alloc] initWithTarget:self selector:@selector(storeKeyToDisk:) object:key] autorelease]]; - } } @@ -185,17 +225,54 @@ static SDImageCache *instance; if (!image && fromDisk) { - image = [[UIImage alloc] initWithContentsOfFile:[self cachePathForKey:key]]; - if (image != nil) + image = [[[UIImage alloc] initWithContentsOfFile:[self cachePathForKey:key]] autorelease]; + if (image) { [memCache setObject:image forKey:key]; - [image autorelease]; } } return image; } +- (void)queryDiskCacheForKey:(NSString *)key delegate:(id )delegate userInfo:(NSDictionary *)info +{ + if (!delegate) + { + return; + } + + if (!key) + { + if ([delegate respondsToSelector:@selector(imageCache:didNotFindImageForKey:userInfo:)]) + { + [delegate imageCache:self didNotFindImageForKey:key userInfo:info]; + } + return; + } + + // First check the in-memory cache... + UIImage *image = [memCache objectForKey:key]; + if (image) + { + // ...notify delegate immediately, no need to go async + if ([delegate respondsToSelector:@selector(imageCache:didFindImage:forKey:userInfo:)]) + { + [delegate imageCache:self didFindImage:image forKey:key userInfo:info]; + } + return; + } + + NSMutableDictionary *arguments = [NSMutableDictionary dictionaryWithCapacity:3]; + [arguments setObject:key forKey:@"key"]; + [arguments setObject:delegate forKey:@"delegate"]; + if (info) + { + [arguments setObject:info forKey:@"userInfo"]; + } + [cacheOutQueue addOperation:[[[NSInvocationOperation alloc] initWithTarget:self selector:@selector(queryDiskCacheOperation:) object:arguments] autorelease]]; +} + - (void)removeImageForKey:(NSString *)key { if (key == nil) diff --git a/SDImageCacheDelegate.h b/SDImageCacheDelegate.h new file mode 100644 index 0000000..947a90a --- /dev/null +++ b/SDImageCacheDelegate.h @@ -0,0 +1,19 @@ +// +// SDImageCacheDelegate.h +// Dailymotion +// +// Created by Olivier Poitrey on 16/09/10. +// Copyright 2010 Dailymotion. All rights reserved. +// + +#import + +@class SDImageCache; + +@protocol SDImageCacheDelegate + +@optional +- (void)imageCache:(SDImageCache *)imageCache didFindImage:(UIImage *)image forKey:(NSString *)key userInfo:(NSDictionary *)info; +- (void)imageCache:(SDImageCache *)imageCache didNotFindImageForKey:(NSString *)key userInfo:(NSDictionary *)info; + +@end diff --git a/SDWebImageManager.h b/SDWebImageManager.h index 749f6b0..4b8289e 100644 --- a/SDWebImageManager.h +++ b/SDWebImageManager.h @@ -9,8 +9,9 @@ #import #import "SDWebImageDownloaderDelegate.h" #import "SDWebImageManagerDelegate.h" +#import "SDImageCacheDelegate.h" -@interface SDWebImageManager : NSObject +@interface SDWebImageManager : NSObject { NSMutableArray *delegates; NSMutableArray *downloaders; diff --git a/SDWebImageManager.m b/SDWebImageManager.m index 7c02b18..5e7e0b8 100644 --- a/SDWebImageManager.m +++ b/SDWebImageManager.m @@ -46,6 +46,9 @@ static SDWebImageManager *instance; return instance; } +/** + * @deprecated + */ - (UIImage *)imageWithURL:(NSURL *)url { return [[SDImageCache sharedImageCache] imageFromKey:[url absoluteString]]; @@ -53,22 +56,14 @@ static SDWebImageManager *instance; - (void)downloadWithURL:(NSURL *)url delegate:(id)delegate { - if (url == nil || [failedURLs containsObject:url]) + if (!url || !delegate || [failedURLs containsObject:url]) { return; } - // Share the same downloader for identical URLs so we don't download the same URL several times - SDWebImageDownloader *downloader = [downloaderForURL objectForKey:url]; - - if (!downloader) - { - downloader = [SDWebImageDownloader downloaderWithURL:url delegate:self]; - [downloaderForURL setObject:downloader forKey:url]; - } - - [delegates addObject:delegate]; - [downloaders addObject:downloader]; + // Check the on-disk cache async so we don't block the main thread + NSDictionary *info = [NSDictionary dictionaryWithObjectsAndKeys:delegate, @"delegate", url, @"url", nil]; + [[SDImageCache sharedImageCache] queryDiskCacheForKey:[url absoluteString] delegate:self userInfo:info]; } - (void)cancelForDelegate:(id)delegate @@ -95,6 +90,37 @@ static SDWebImageManager *instance; [downloader release]; } +#pragma mark SDImageCacheDelegate + +- (void)imageCache:(SDImageCache *)imageCache didFindImage:(UIImage *)image forKey:(NSString *)key userInfo:(NSDictionary *)info +{ + id delegate = [info objectForKey:@"delegate"]; + if ([delegate respondsToSelector:@selector(webImageManager:didFinishWithImage:)]) + { + [delegate performSelector:@selector(webImageManager:didFinishWithImage:) withObject:self withObject:image]; + } +} + +- (void)imageCache:(SDImageCache *)imageCache didNotFindImageForKey:(NSString *)key userInfo:(NSDictionary *)info +{ + NSURL *url = [info objectForKey:@"url"]; + id delegate = [info objectForKey:@"delegate"]; + + // Share the same downloader for identical URLs so we don't download the same URL several times + SDWebImageDownloader *downloader = [downloaderForURL objectForKey:url]; + + if (!downloader) + { + downloader = [SDWebImageDownloader downloaderWithURL:url delegate:self]; + [downloaderForURL setObject:downloader forKey:url]; + } + + [delegates addObject:delegate]; + [downloaders addObject:downloader]; +} + +#pragma mark SDWebImageDownloaderDelegate + - (void)imageDownloader:(SDWebImageDownloader *)downloader didFinishWithImage:(UIImage *)image { [downloader retain]; diff --git a/UIImageView+WebCache.m b/UIImageView+WebCache.m index 4f95d7a..7b70dd1 100644 --- a/UIImageView+WebCache.m +++ b/UIImageView+WebCache.m @@ -23,31 +23,11 @@ // Remove in progress downloader from queue [manager cancelForDelegate:self]; - UIImage *cachedImage = nil; + self.image = placeholder; + if (url) { - cachedImage = [manager imageWithURL:url]; - } - else - { - self.image = placeholder; - } - - if (cachedImage) - { - self.image = cachedImage; - } - else - { - if (placeholder) - { - self.image = placeholder; - } - - if (url) - { - [manager downloadWithURL:url delegate:self]; - } + [manager downloadWithURL:url delegate:self]; } }