/* * This file is part of the SDWebImage package. * (c) Olivier Poitrey * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ #import "SDImageCache.h" #import static NSInteger cacheMaxCacheAge = 60*60*24*7; // 1 week static SDImageCache *instance; @implementation SDImageCache #pragma mark NSObject - (id)init { if (self = [super init]) { // Init the memory cache memCache = [[NSMutableDictionary alloc] init]; // Init the disk cache NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES); diskCachePath = [[[paths objectAtIndex:0] stringByAppendingPathComponent:@"ImageCache"] retain]; if (![[NSFileManager defaultManager] fileExistsAtPath:diskCachePath]) { [[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; } - (void)dealloc { [memCache release]; [diskCachePath release]; [cacheInQueue release]; [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidReceiveMemoryWarningNotification object:nil]; [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationWillTerminateNotification object:nil]; [super dealloc]; } - (void)didReceiveMemoryWarning:(void *)object { [self clearMemory]; } - (void)willTerminate { [self cleanDisk]; } #pragma mark ImageCache (class methods) + (SDImageCache *)sharedImageCache { if (instance == nil) { instance = [[SDImageCache alloc] init]; } return instance; } #pragma mark ImageCache (private) - (NSString *)cachePathForKey:(NSString *)key { const char *str = [key UTF8String]; unsigned char r[CC_MD5_DIGEST_LENGTH]; CC_MD5(str, strlen(str), r); NSString *filename = [NSString stringWithFormat:@"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", r[0], r[1], r[2], r[3], r[4], r[5], r[6], r[7], r[8], r[9], r[10], r[11], r[12], r[13], r[14], r[15]]; return [diskCachePath stringByAppendingPathComponent:filename]; } - (void)storeKeyToDisk:(NSString *)key { UIImage *image = [[self imageFromKey:key fromDisk:YES] retain]; // be thread safe with no lock 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]; } - (void)storeImage:(UIImage *)image forKey:(NSString *)key toDisk:(BOOL)toDisk { if (image == nil) { return; } [memCache setObject:image forKey:key]; if (toDisk) { [cacheInQueue addOperation:[[[NSInvocationOperation alloc] initWithTarget:self selector:@selector(storeKeyToDisk:) object:key] autorelease]]; } } - (UIImage *)imageFromKey:(NSString *)key { return [self imageFromKey:key fromDisk:YES]; } - (UIImage *)imageFromKey:(NSString *)key fromDisk:(BOOL)fromDisk { UIImage *image = [memCache objectForKey:key]; if (!image && fromDisk) { image = [[UIImage alloc] initWithData:[NSData dataWithContentsOfFile:[self cachePathForKey:key]]]; if (image != nil) { [memCache setObject:image forKey:key]; [image release]; } } return image; } - (void)removeImageForKey:(NSString *)key { [memCache removeObjectForKey:key]; [[NSFileManager defaultManager] removeItemAtPath:[self cachePathForKey:key] error:nil]; } - (void)clearMemory { [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:-cacheMaxCacheAge]; NSDirectoryEnumerator *fileEnumerator = [[NSFileManager defaultManager] enumeratorAtPath:diskCachePath]; for (NSString *fileName in fileEnumerator) { NSString *filePath = [diskCachePath stringByAppendingPathComponent:fileName]; NSDictionary *attrs = [[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:nil]; if ([[[attrs fileModificationDate] laterDate:expirationDate] isEqualToDate:expirationDate]) { [[NSFileManager defaultManager] removeItemAtPath:filePath error:nil]; } } } @end