Split DMWebImageDownloader from DMWebImageView, and refactor so each class maintain its own operation
This commit is contained in:
parent
89b45c3f85
commit
6d1eaa4c88
|
@ -10,8 +10,9 @@
|
|||
|
||||
@interface DMImageCache : NSObject
|
||||
{
|
||||
NSMutableDictionary *cache;
|
||||
NSMutableDictionary *memCache;
|
||||
NSString *diskCachePath;
|
||||
NSOperationQueue *cacheInQueue;
|
||||
}
|
||||
|
||||
+ (DMImageCache *)sharedImageCache;
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
#import "DMImageCache.h"
|
||||
#import <CommonCrypto/CommonDigest.h>
|
||||
|
||||
static NSInteger kMaxCacheAge = 60*60*24*7; // 1 week
|
||||
static NSInteger cacheMaxCacheAge = 60*60*24*7; // 1 week
|
||||
|
||||
static DMImageCache *instance;
|
||||
|
||||
|
@ -21,19 +21,10 @@ static DMImageCache *instance;
|
|||
{
|
||||
if (self = [super init])
|
||||
{
|
||||
cache = [[NSMutableDictionary alloc] init];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(didReceiveMemoryWarning:)
|
||||
name:UIApplicationDidReceiveMemoryWarningNotification
|
||||
object:nil];
|
||||
// Init the memory cache
|
||||
memCache = [[NSMutableDictionary alloc] init];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(willTerminate)
|
||||
name:UIApplicationWillTerminateNotification
|
||||
object:nil];
|
||||
|
||||
// Init the cache
|
||||
// Init the disk cache
|
||||
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
|
||||
diskCachePath = [[[paths objectAtIndex:0] stringByAppendingPathComponent:@"ImageCache"] retain];
|
||||
|
||||
|
@ -41,6 +32,21 @@ static DMImageCache *instance;
|
|||
{
|
||||
[[NSFileManager defaultManager] createDirectoryAtPath:diskCachePath attributes:nil];
|
||||
}
|
||||
|
||||
// Init the operation queue
|
||||
cacheInQueue = [[NSOperationQueue alloc] init];
|
||||
cacheInQueue.maxConcurrentOperationCount = 2;
|
||||
|
||||
// Subscribe to app events
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(didReceiveMemoryWarning:)
|
||||
name:UIApplicationDidReceiveMemoryWarningNotification
|
||||
object:nil];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(willTerminate)
|
||||
name:UIApplicationWillTerminateNotification
|
||||
object:nil];
|
||||
}
|
||||
|
||||
return self;
|
||||
|
@ -48,7 +54,9 @@ static DMImageCache *instance;
|
|||
|
||||
- (void)dealloc
|
||||
{
|
||||
[cache release];
|
||||
[memCache release];
|
||||
[diskCachePath release];
|
||||
[cacheInQueue release];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self
|
||||
name:UIApplicationDidReceiveMemoryWarningNotification
|
||||
|
@ -71,6 +79,18 @@ static DMImageCache *instance;
|
|||
[self cleanDisk];
|
||||
}
|
||||
|
||||
#pragma mark ImageCache (class methods)
|
||||
|
||||
+ (DMImageCache *)sharedImageCache
|
||||
{
|
||||
if (instance == nil)
|
||||
{
|
||||
instance = [[DMImageCache alloc] init];
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
#pragma mark ImageCache (private)
|
||||
|
||||
- (NSString *)cachePathForKey:(NSString *)key
|
||||
|
@ -84,18 +104,19 @@ static DMImageCache *instance;
|
|||
return [diskCachePath stringByAppendingPathComponent:filename];
|
||||
}
|
||||
|
||||
#pragma mark ImageCache
|
||||
|
||||
+ (DMImageCache *)sharedImageCache
|
||||
- (void)storeKeyToDisk:(NSString *)key
|
||||
{
|
||||
if (instance == nil)
|
||||
{
|
||||
instance = [[DMImageCache alloc] init];
|
||||
}
|
||||
UIImage *image = [[self imageFromKey:key fromDisk:YES] retain]; // be thread safe with no lock
|
||||
|
||||
return instance;
|
||||
if (image != nil)
|
||||
{
|
||||
[[NSFileManager defaultManager] createFileAtPath:[self cachePathForKey:key] contents:UIImageJPEGRepresentation(image, 1.0) attributes:nil];
|
||||
[image release];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark ImageCache
|
||||
|
||||
- (void)storeImage:(UIImage *)image forKey:(NSString *)key
|
||||
{
|
||||
[self storeImage:image forKey:key toDisk:YES];
|
||||
|
@ -108,11 +129,11 @@ static DMImageCache *instance;
|
|||
return;
|
||||
}
|
||||
|
||||
[cache setObject:image forKey:key];
|
||||
[memCache setObject:image forKey:key];
|
||||
|
||||
if (toDisk)
|
||||
{
|
||||
[[NSFileManager defaultManager] createFileAtPath:[self cachePathForKey:key] contents:UIImageJPEGRepresentation(image, 1.0) attributes:nil];
|
||||
{
|
||||
[cacheInQueue addOperation:[[[NSInvocationOperation alloc] initWithTarget:self selector:@selector(storeKeyToDisk:) object:key] autorelease]];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -123,14 +144,14 @@ static DMImageCache *instance;
|
|||
|
||||
- (UIImage *)imageFromKey:(NSString *)key fromDisk:(BOOL)fromDisk
|
||||
{
|
||||
UIImage *image = [cache objectForKey:key];
|
||||
UIImage *image = [memCache objectForKey:key];
|
||||
|
||||
if (!image && fromDisk)
|
||||
{
|
||||
image = [[UIImage alloc] initWithData:[NSData dataWithContentsOfFile:[self cachePathForKey:key]]];
|
||||
if (image != nil)
|
||||
{
|
||||
[cache setObject:image forKey:key];
|
||||
[memCache setObject:image forKey:key];
|
||||
[image release];
|
||||
}
|
||||
}
|
||||
|
@ -140,24 +161,26 @@ static DMImageCache *instance;
|
|||
|
||||
- (void)removeImageForKey:(NSString *)key
|
||||
{
|
||||
[cache removeObjectForKey:key];
|
||||
[memCache removeObjectForKey:key];
|
||||
[[NSFileManager defaultManager] removeItemAtPath:[self cachePathForKey:key] error:nil];
|
||||
}
|
||||
|
||||
- (void)clearMemory
|
||||
{
|
||||
[cache removeAllObjects];
|
||||
[cacheInQueue cancelAllOperations]; // won't be able to complete
|
||||
[memCache removeAllObjects];
|
||||
}
|
||||
|
||||
- (void)clearDisk
|
||||
{
|
||||
[cacheInQueue cancelAllOperations];
|
||||
[[NSFileManager defaultManager] removeItemAtPath:diskCachePath error:nil];
|
||||
[[NSFileManager defaultManager] createDirectoryAtPath:diskCachePath attributes:nil];
|
||||
}
|
||||
|
||||
- (void)cleanDisk
|
||||
{
|
||||
NSDate *expirationDate = [NSDate dateWithTimeIntervalSinceNow:-kMaxCacheAge];
|
||||
NSDate *expirationDate = [NSDate dateWithTimeIntervalSinceNow:-cacheMaxCacheAge];
|
||||
NSDirectoryEnumerator *fileEnumerator = [[NSFileManager defaultManager] enumeratorAtPath:diskCachePath];
|
||||
for (NSString *fileName in fileEnumerator)
|
||||
{
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* This file is part of the DMWebImage package.
|
||||
* (c) Dailymotion - Olivier Poitrey <rs@dailymotion.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
|
||||
@interface DMWebImageDownloader : NSOperation
|
||||
{
|
||||
NSURL *url;
|
||||
id target;
|
||||
SEL action;
|
||||
}
|
||||
|
||||
@property (retain) NSURL *url;
|
||||
@property (assign) id target;
|
||||
@property (assign) SEL action;
|
||||
|
||||
+ (id)downloaderWithURL:(NSURL *)url target:(id)target action:(SEL)action;
|
||||
+ (void)setMaxConcurrentDownloads:(NSUInteger)max;
|
||||
|
||||
@end
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* This file is part of the DMWebImage package.
|
||||
* (c) Dailymotion - Olivier Poitrey <rs@dailymotion.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
#import "DMWebImageDownloader.h"
|
||||
#import "DMImageCache.h"
|
||||
|
||||
static NSOperationQueue *queue;
|
||||
|
||||
@implementation DMWebImageDownloader
|
||||
|
||||
@synthesize url, target, action;
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[url release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
+ (id)downloaderWithURL:(NSURL *)url target:(id)target action:(SEL)action
|
||||
{
|
||||
DMWebImageDownloader *downloader = [[[DMWebImageDownloader alloc] init] autorelease];
|
||||
downloader.url = url;
|
||||
downloader.target = target;
|
||||
downloader.action = action;
|
||||
|
||||
if (queue == nil)
|
||||
{
|
||||
queue = [[NSOperationQueue alloc] init];
|
||||
queue.maxConcurrentOperationCount = 8;
|
||||
}
|
||||
|
||||
[queue addOperation:downloader];
|
||||
|
||||
return downloader;
|
||||
}
|
||||
|
||||
+ (void)setMaxConcurrentDownloads:(NSUInteger)max
|
||||
{
|
||||
if (queue == nil)
|
||||
{
|
||||
queue = [[NSOperationQueue alloc] init];
|
||||
}
|
||||
|
||||
queue.maxConcurrentOperationCount = max;
|
||||
}
|
||||
|
||||
- (void)main
|
||||
{
|
||||
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
|
||||
|
||||
UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:url]];
|
||||
|
||||
if (!self.isCancelled)
|
||||
{
|
||||
[target performSelector:action withObject:image];
|
||||
}
|
||||
|
||||
[[DMImageCache sharedImageCache] storeImage:image forKey:[url absoluteString]];
|
||||
|
||||
[pool release];
|
||||
}
|
||||
|
||||
@end
|
|
@ -8,28 +8,15 @@
|
|||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@class DMWebImageDownloadOperation;
|
||||
@class DMWebImageDownloader;
|
||||
|
||||
@interface DMWebImageView : UIImageView
|
||||
{
|
||||
UIImage *placeHolderImage;
|
||||
DMWebImageDownloadOperation *currentOperation;
|
||||
DMWebImageDownloader *currentOperation;
|
||||
}
|
||||
|
||||
- (void)setImageWithURL:(NSURL *)url;
|
||||
- (void)downloadFinishedWithImage:(UIImage *)image;
|
||||
|
||||
@end
|
||||
|
||||
@interface DMWebImageDownloadOperation : NSOperation
|
||||
{
|
||||
NSURL *url;
|
||||
DMWebImageView *delegate;
|
||||
}
|
||||
|
||||
@property (retain) NSURL *url;
|
||||
@property (assign) DMWebImageView *delegate;
|
||||
|
||||
- (id)initWithURL:(NSURL *)url delegate:(DMWebImageView *)delegate;
|
||||
|
||||
@end
|
||||
@end
|
|
@ -8,9 +8,7 @@
|
|||
|
||||
#import "DMWebImageView.h"
|
||||
#import "DMImageCache.h"
|
||||
|
||||
static NSOperationQueue *downloadQueue;
|
||||
static NSOperationQueue *cacheInQueue;
|
||||
#import "DMWebImageDownloader.h"
|
||||
|
||||
@implementation DMWebImageView
|
||||
|
||||
|
@ -49,15 +47,8 @@ static NSOperationQueue *cacheInQueue;
|
|||
self.image = cachedImage;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (downloadQueue == nil)
|
||||
{
|
||||
downloadQueue = [[NSOperationQueue alloc] init];
|
||||
[downloadQueue setMaxConcurrentOperationCount:8];
|
||||
}
|
||||
|
||||
currentOperation = [[DMWebImageDownloadOperation alloc] initWithURL:url delegate:self];
|
||||
[downloadQueue addOperation:currentOperation];
|
||||
{
|
||||
currentOperation = [[DMWebImageDownloader downloaderWithURL:url target:self action:@selector(downloadFinishedWithImage:)] retain];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -69,70 +60,3 @@ static NSOperationQueue *cacheInQueue;
|
|||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation DMWebImageDownloadOperation
|
||||
|
||||
@synthesize url, delegate;
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[url release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
|
||||
- (id)initWithURL:(NSURL *)anUrl delegate:(DMWebImageView *)aDelegate
|
||||
{
|
||||
if (self = [super init])
|
||||
{
|
||||
self.url = anUrl;
|
||||
self.delegate = aDelegate;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)main
|
||||
{
|
||||
if (self.isCancelled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
NSData *data = [[NSData alloc] initWithContentsOfURL:url];
|
||||
UIImage *image = [[UIImage alloc] initWithData:data];
|
||||
[data release];
|
||||
|
||||
if (!self.isCancelled)
|
||||
{
|
||||
[delegate performSelectorOnMainThread:@selector(downloadFinishedWithImage:) withObject:image waitUntilDone:YES];
|
||||
}
|
||||
|
||||
if (cacheInQueue == nil)
|
||||
{
|
||||
cacheInQueue = [[NSOperationQueue alloc] init];
|
||||
[cacheInQueue setMaxConcurrentOperationCount:2];
|
||||
}
|
||||
|
||||
NSString *cacheKey = [url absoluteString];
|
||||
|
||||
DMImageCache *imageCache = [DMImageCache sharedImageCache];
|
||||
|
||||
// Store image in memory cache NOW, no need to wait for the cache-in operation queue completion
|
||||
[imageCache storeImage:image forKey:cacheKey toDisk:NO];
|
||||
|
||||
// Perform the cache-in in another operation queue in order to not block a download operation slot
|
||||
NSInvocation *cacheInInvocation = [NSInvocation invocationWithMethodSignature:[[imageCache class] instanceMethodSignatureForSelector:@selector(storeImage:forKey:)]];
|
||||
[cacheInInvocation setTarget:imageCache];
|
||||
[cacheInInvocation setSelector:@selector(storeImage:forKey:)];
|
||||
[cacheInInvocation setArgument:&image atIndex:2];
|
||||
[cacheInInvocation setArgument:&cacheKey atIndex:3];
|
||||
[cacheInInvocation retainArguments];
|
||||
NSInvocationOperation *cacheInOperation = [[NSInvocationOperation alloc] initWithInvocation:cacheInInvocation];
|
||||
[cacheInQueue addOperation:cacheInOperation];
|
||||
[cacheInOperation release];
|
||||
|
||||
[image release];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
Loading…
Reference in New Issue