Do not convert images to JPEG when stored to disk for caching

This saves CPU and memory in all cases and alpha channel / image clearness if orignal format was PNG or GIF.
This commit is contained in:
Olivier Poitrey 2010-08-29 02:56:47 +02:00
parent 7f7dfbecd6
commit d2dc4cbbac
6 changed files with 59 additions and 18 deletions

View File

@ -10,7 +10,7 @@
@interface SDImageCache : NSObject @interface SDImageCache : NSObject
{ {
NSMutableDictionary *memCache; NSMutableDictionary *memCache, *storeDataQueue;
NSString *diskCachePath; NSString *diskCachePath;
NSOperationQueue *cacheInQueue; NSOperationQueue *cacheInQueue;
} }
@ -18,6 +18,7 @@
+ (SDImageCache *)sharedImageCache; + (SDImageCache *)sharedImageCache;
- (void)storeImage:(UIImage *)image forKey:(NSString *)key; - (void)storeImage:(UIImage *)image forKey:(NSString *)key;
- (void)storeImage:(UIImage *)image forKey:(NSString *)key toDisk:(BOOL)toDisk; - (void)storeImage:(UIImage *)image forKey:(NSString *)key toDisk:(BOOL)toDisk;
- (void)storeImage:(UIImage *)image imageData:(NSData *)data forKey:(NSString *)key toDisk:(BOOL)toDisk;
- (UIImage *)imageFromKey:(NSString *)key; - (UIImage *)imageFromKey:(NSString *)key;
- (UIImage *)imageFromKey:(NSString *)key fromDisk:(BOOL)fromDisk; - (UIImage *)imageFromKey:(NSString *)key fromDisk:(BOOL)fromDisk;
- (void)removeImageForKey:(NSString *)key; - (void)removeImageForKey:(NSString *)key;

View File

@ -25,6 +25,7 @@ static SDImageCache *instance;
memCache = [[NSMutableDictionary alloc] init]; memCache = [[NSMutableDictionary alloc] init];
// Init the disk cache // Init the disk cache
storeDataQueue = [[NSMutableDictionary alloc] init];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES); NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
diskCachePath = [[[paths objectAtIndex:0] stringByAppendingPathComponent:@"ImageCache"] retain]; diskCachePath = [[[paths objectAtIndex:0] stringByAppendingPathComponent:@"ImageCache"] retain];
@ -72,6 +73,7 @@ static SDImageCache *instance;
[memCache release], memCache = nil; [memCache release], memCache = nil;
[diskCachePath release], diskCachePath = nil; [diskCachePath release], diskCachePath = nil;
[cacheInQueue release], cacheInQueue = nil; [cacheInQueue release], cacheInQueue = nil;
[storeDataQueue release], storeDataQueue = nil;
[[NSNotificationCenter defaultCenter] removeObserver:self]; [[NSNotificationCenter defaultCenter] removeObserver:self];
@ -105,25 +107,43 @@ static SDImageCache *instance;
- (void)storeKeyToDisk:(NSString *)key - (void)storeKeyToDisk:(NSString *)key
{ {
UIImage *image = [[self imageFromKey:key fromDisk:YES] retain]; // be thread safe with no lock // Can't use defaultManager another thread
NSFileManager *fileManager = [[NSFileManager alloc] init];
if (image != nil) NSData *data = [storeDataQueue objectForKey:key];
if (data)
{ {
[[NSFileManager defaultManager] createFileAtPath:[self cachePathForKey:key] contents:UIImageJPEGRepresentation(image, (CGFloat)1.0) attributes:nil]; [fileManager createFileAtPath:[self cachePathForKey:key] contents:data attributes:nil];
[image release]; @synchronized(storeDataQueue)
{
[storeDataQueue removeObjectForKey:key];
}
} }
else
{
// If no data representation given, convert the UIImage in JPEG and store it
// This trick is more CPU/memory intensive and doesn't preserve alpha channel
UIImage *image = [[self imageFromKey:key fromDisk:YES] retain]; // be thread safe with no lock
if (image)
{
[fileManager createFileAtPath:[self cachePathForKey:key] contents:UIImageJPEGRepresentation(image, (CGFloat)1.0) attributes:nil];
[image release];
}
}
[fileManager release];
} }
#pragma mark ImageCache #pragma mark ImageCache
- (void)storeImage:(UIImage *)image forKey:(NSString *)key - (void)storeImage:(UIImage *)image imageData:(NSData *)data forKey:(NSString *)key toDisk:(BOOL)toDisk
{ {
[self storeImage:image forKey:key toDisk:YES]; if (!image || !key)
} {
return;
}
- (void)storeImage:(UIImage *)image forKey:(NSString *)key toDisk:(BOOL)toDisk if (toDisk && !data)
{
if (image == nil || key == nil)
{ {
return; return;
} }
@ -132,10 +152,23 @@ static SDImageCache *instance;
if (toDisk) if (toDisk)
{ {
[storeDataQueue setObject:data forKey:key];
[cacheInQueue addOperation:[[[NSInvocationOperation alloc] initWithTarget:self selector:@selector(storeKeyToDisk:) object:key] autorelease]]; [cacheInQueue addOperation:[[[NSInvocationOperation alloc] initWithTarget:self selector:@selector(storeKeyToDisk:) object:key] autorelease]];
} }
} }
- (void)storeImage:(UIImage *)image forKey:(NSString *)key
{
[self storeImage:image imageData:nil forKey:key toDisk:YES];
}
- (void)storeImage:(UIImage *)image forKey:(NSString *)key toDisk:(BOOL)toDisk
{
[self storeImage:image imageData:nil forKey:key toDisk:toDisk];
}
- (UIImage *)imageFromKey:(NSString *)key - (UIImage *)imageFromKey:(NSString *)key
{ {
return [self imageFromKey:key fromDisk:YES]; return [self imageFromKey:key fromDisk:YES];
@ -152,7 +185,7 @@ static SDImageCache *instance;
if (!image && fromDisk) if (!image && fromDisk)
{ {
image = [[UIImage alloc] initWithData:[NSData dataWithContentsOfFile:[self cachePathForKey:key]]]; image = [[UIImage alloc] initWithContentsOfFile:[self cachePathForKey:key]];
if (image != nil) if (image != nil)
{ {
[memCache setObject:image forKey:key]; [memCache setObject:image forKey:key];

View File

@ -20,6 +20,7 @@
@property (nonatomic, retain) NSURL *url; @property (nonatomic, retain) NSURL *url;
@property (nonatomic, assign) id<SDWebImageDownloaderDelegate> delegate; @property (nonatomic, assign) id<SDWebImageDownloaderDelegate> delegate;
@property (nonatomic, retain) NSMutableData *imageData;
+ (id)downloaderWithURL:(NSURL *)url delegate:(id<SDWebImageDownloaderDelegate>)delegate; + (id)downloaderWithURL:(NSURL *)url delegate:(id<SDWebImageDownloaderDelegate>)delegate;
- (void)start; - (void)start;

View File

@ -10,7 +10,6 @@
@interface SDWebImageDownloader () @interface SDWebImageDownloader ()
@property (nonatomic, retain) NSURLConnection *connection; @property (nonatomic, retain) NSURLConnection *connection;
@property (nonatomic, retain) NSMutableData *imageData;
@end @end
@implementation SDWebImageDownloader @implementation SDWebImageDownloader
@ -73,16 +72,19 @@
- (void)connectionDidFinishLoading:(NSURLConnection *)aConnection - (void)connectionDidFinishLoading:(NSURLConnection *)aConnection
{ {
UIImage *image = [[UIImage alloc] initWithData:imageData];
self.imageData = nil;
self.connection = nil; self.connection = nil;
if ([delegate respondsToSelector:@selector(imageDownloaderDidFinish:)])
{
[delegate performSelector:@selector(imageDownloaderDidFinish:) withObject:self];
}
if ([delegate respondsToSelector:@selector(imageDownloader:didFinishWithImage:)]) if ([delegate respondsToSelector:@selector(imageDownloader:didFinishWithImage:)])
{ {
UIImage *image = [[UIImage alloc] initWithData:imageData];
[delegate performSelector:@selector(imageDownloader:didFinishWithImage:) withObject:self withObject:image]; [delegate performSelector:@selector(imageDownloader:didFinishWithImage:) withObject:self withObject:image];
[image release];
} }
[image release];
} }
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error

View File

@ -12,6 +12,7 @@
@optional @optional
- (void)imageDownloaderDidFinish:(SDWebImageDownloader *)downloader;
- (void)imageDownloader:(SDWebImageDownloader *)downloader didFinishWithImage:(UIImage *)image; - (void)imageDownloader:(SDWebImageDownloader *)downloader didFinishWithImage:(UIImage *)image;
- (void)imageDownloader:(SDWebImageDownloader *)downloader didFailWithError:(NSError *)error; - (void)imageDownloader:(SDWebImageDownloader *)downloader didFailWithError:(NSError *)error;

View File

@ -120,7 +120,10 @@ static SDWebImageManager *instance;
if (image) if (image)
{ {
// Store the image in the cache // Store the image in the cache
[[SDImageCache sharedImageCache] storeImage:image forKey:[downloader.url absoluteString]]; [[SDImageCache sharedImageCache] storeImage:image
imageData:downloader.imageData
forKey:[downloader.url absoluteString]
toDisk:YES];
} }
else else
{ {