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.
Showing
6 changed files
with
58 additions
and
17 deletions
@@ -10,7 +10,7 @@ | @@ -10,7 +10,7 @@ | ||
10 | 10 | ||
11 | @interface SDImageCache : NSObject | 11 | @interface SDImageCache : NSObject |
12 | { | 12 | { |
13 | - NSMutableDictionary *memCache; | 13 | + NSMutableDictionary *memCache, *storeDataQueue; |
14 | NSString *diskCachePath; | 14 | NSString *diskCachePath; |
15 | NSOperationQueue *cacheInQueue; | 15 | NSOperationQueue *cacheInQueue; |
16 | } | 16 | } |
@@ -18,6 +18,7 @@ | @@ -18,6 +18,7 @@ | ||
18 | + (SDImageCache *)sharedImageCache; | 18 | + (SDImageCache *)sharedImageCache; |
19 | - (void)storeImage:(UIImage *)image forKey:(NSString *)key; | 19 | - (void)storeImage:(UIImage *)image forKey:(NSString *)key; |
20 | - (void)storeImage:(UIImage *)image forKey:(NSString *)key toDisk:(BOOL)toDisk; | 20 | - (void)storeImage:(UIImage *)image forKey:(NSString *)key toDisk:(BOOL)toDisk; |
21 | +- (void)storeImage:(UIImage *)image imageData:(NSData *)data forKey:(NSString *)key toDisk:(BOOL)toDisk; | ||
21 | - (UIImage *)imageFromKey:(NSString *)key; | 22 | - (UIImage *)imageFromKey:(NSString *)key; |
22 | - (UIImage *)imageFromKey:(NSString *)key fromDisk:(BOOL)fromDisk; | 23 | - (UIImage *)imageFromKey:(NSString *)key fromDisk:(BOOL)fromDisk; |
23 | - (void)removeImageForKey:(NSString *)key; | 24 | - (void)removeImageForKey:(NSString *)key; |
@@ -25,6 +25,7 @@ static SDImageCache *instance; | @@ -25,6 +25,7 @@ static SDImageCache *instance; | ||
25 | memCache = [[NSMutableDictionary alloc] init]; | 25 | memCache = [[NSMutableDictionary alloc] init]; |
26 | 26 | ||
27 | // Init the disk cache | 27 | // Init the disk cache |
28 | + storeDataQueue = [[NSMutableDictionary alloc] init]; | ||
28 | NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES); | 29 | NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES); |
29 | diskCachePath = [[[paths objectAtIndex:0] stringByAppendingPathComponent:@"ImageCache"] retain]; | 30 | diskCachePath = [[[paths objectAtIndex:0] stringByAppendingPathComponent:@"ImageCache"] retain]; |
30 | 31 | ||
@@ -72,6 +73,7 @@ static SDImageCache *instance; | @@ -72,6 +73,7 @@ static SDImageCache *instance; | ||
72 | [memCache release], memCache = nil; | 73 | [memCache release], memCache = nil; |
73 | [diskCachePath release], diskCachePath = nil; | 74 | [diskCachePath release], diskCachePath = nil; |
74 | [cacheInQueue release], cacheInQueue = nil; | 75 | [cacheInQueue release], cacheInQueue = nil; |
76 | + [storeDataQueue release], storeDataQueue = nil; | ||
75 | 77 | ||
76 | [[NSNotificationCenter defaultCenter] removeObserver:self]; | 78 | [[NSNotificationCenter defaultCenter] removeObserver:self]; |
77 | 79 | ||
@@ -105,25 +107,43 @@ static SDImageCache *instance; | @@ -105,25 +107,43 @@ static SDImageCache *instance; | ||
105 | 107 | ||
106 | - (void)storeKeyToDisk:(NSString *)key | 108 | - (void)storeKeyToDisk:(NSString *)key |
107 | { | 109 | { |
108 | - UIImage *image = [[self imageFromKey:key fromDisk:YES] retain]; // be thread safe with no lock | 110 | + // Can't use defaultManager another thread |
111 | + NSFileManager *fileManager = [[NSFileManager alloc] init]; | ||
109 | 112 | ||
110 | - if (image != nil) | 113 | + NSData *data = [storeDataQueue objectForKey:key]; |
114 | + if (data) | ||
115 | + { | ||
116 | + [fileManager createFileAtPath:[self cachePathForKey:key] contents:data attributes:nil]; | ||
117 | + @synchronized(storeDataQueue) | ||
118 | + { | ||
119 | + [storeDataQueue removeObjectForKey:key]; | ||
120 | + } | ||
121 | + } | ||
122 | + else | ||
123 | + { | ||
124 | + // If no data representation given, convert the UIImage in JPEG and store it | ||
125 | + // This trick is more CPU/memory intensive and doesn't preserve alpha channel | ||
126 | + UIImage *image = [[self imageFromKey:key fromDisk:YES] retain]; // be thread safe with no lock | ||
127 | + if (image) | ||
111 | { | 128 | { |
112 | - [[NSFileManager defaultManager] createFileAtPath:[self cachePathForKey:key] contents:UIImageJPEGRepresentation(image, (CGFloat)1.0) attributes:nil]; | 129 | + [fileManager createFileAtPath:[self cachePathForKey:key] contents:UIImageJPEGRepresentation(image, (CGFloat)1.0) attributes:nil]; |
113 | [image release]; | 130 | [image release]; |
114 | } | 131 | } |
132 | + } | ||
133 | + | ||
134 | + [fileManager release]; | ||
115 | } | 135 | } |
116 | 136 | ||
117 | #pragma mark ImageCache | 137 | #pragma mark ImageCache |
118 | 138 | ||
119 | -- (void)storeImage:(UIImage *)image forKey:(NSString *)key | 139 | +- (void)storeImage:(UIImage *)image imageData:(NSData *)data forKey:(NSString *)key toDisk:(BOOL)toDisk |
120 | { | 140 | { |
121 | - [self storeImage:image forKey:key toDisk:YES]; | ||
122 | -} | 141 | + if (!image || !key) |
142 | + { | ||
143 | + return; | ||
144 | + } | ||
123 | 145 | ||
124 | -- (void)storeImage:(UIImage *)image forKey:(NSString *)key toDisk:(BOOL)toDisk | ||
125 | -{ | ||
126 | - if (image == nil || key == nil) | 146 | + if (toDisk && !data) |
127 | { | 147 | { |
128 | return; | 148 | return; |
129 | } | 149 | } |
@@ -132,10 +152,23 @@ static SDImageCache *instance; | @@ -132,10 +152,23 @@ static SDImageCache *instance; | ||
132 | 152 | ||
133 | if (toDisk) | 153 | if (toDisk) |
134 | { | 154 | { |
155 | + [storeDataQueue setObject:data forKey:key]; | ||
135 | [cacheInQueue addOperation:[[[NSInvocationOperation alloc] initWithTarget:self selector:@selector(storeKeyToDisk:) object:key] autorelease]]; | 156 | [cacheInQueue addOperation:[[[NSInvocationOperation alloc] initWithTarget:self selector:@selector(storeKeyToDisk:) object:key] autorelease]]; |
157 | + | ||
136 | } | 158 | } |
137 | } | 159 | } |
138 | 160 | ||
161 | +- (void)storeImage:(UIImage *)image forKey:(NSString *)key | ||
162 | +{ | ||
163 | + [self storeImage:image imageData:nil forKey:key toDisk:YES]; | ||
164 | +} | ||
165 | + | ||
166 | +- (void)storeImage:(UIImage *)image forKey:(NSString *)key toDisk:(BOOL)toDisk | ||
167 | +{ | ||
168 | + [self storeImage:image imageData:nil forKey:key toDisk:toDisk]; | ||
169 | +} | ||
170 | + | ||
171 | + | ||
139 | - (UIImage *)imageFromKey:(NSString *)key | 172 | - (UIImage *)imageFromKey:(NSString *)key |
140 | { | 173 | { |
141 | return [self imageFromKey:key fromDisk:YES]; | 174 | return [self imageFromKey:key fromDisk:YES]; |
@@ -152,7 +185,7 @@ static SDImageCache *instance; | @@ -152,7 +185,7 @@ static SDImageCache *instance; | ||
152 | 185 | ||
153 | if (!image && fromDisk) | 186 | if (!image && fromDisk) |
154 | { | 187 | { |
155 | - image = [[UIImage alloc] initWithData:[NSData dataWithContentsOfFile:[self cachePathForKey:key]]]; | 188 | + image = [[UIImage alloc] initWithContentsOfFile:[self cachePathForKey:key]]; |
156 | if (image != nil) | 189 | if (image != nil) |
157 | { | 190 | { |
158 | [memCache setObject:image forKey:key]; | 191 | [memCache setObject:image forKey:key]; |
@@ -20,6 +20,7 @@ | @@ -20,6 +20,7 @@ | ||
20 | 20 | ||
21 | @property (nonatomic, retain) NSURL *url; | 21 | @property (nonatomic, retain) NSURL *url; |
22 | @property (nonatomic, assign) id<SDWebImageDownloaderDelegate> delegate; | 22 | @property (nonatomic, assign) id<SDWebImageDownloaderDelegate> delegate; |
23 | +@property (nonatomic, retain) NSMutableData *imageData; | ||
23 | 24 | ||
24 | + (id)downloaderWithURL:(NSURL *)url delegate:(id<SDWebImageDownloaderDelegate>)delegate; | 25 | + (id)downloaderWithURL:(NSURL *)url delegate:(id<SDWebImageDownloaderDelegate>)delegate; |
25 | - (void)start; | 26 | - (void)start; |
@@ -10,7 +10,6 @@ | @@ -10,7 +10,6 @@ | ||
10 | 10 | ||
11 | @interface SDWebImageDownloader () | 11 | @interface SDWebImageDownloader () |
12 | @property (nonatomic, retain) NSURLConnection *connection; | 12 | @property (nonatomic, retain) NSURLConnection *connection; |
13 | -@property (nonatomic, retain) NSMutableData *imageData; | ||
14 | @end | 13 | @end |
15 | 14 | ||
16 | @implementation SDWebImageDownloader | 15 | @implementation SDWebImageDownloader |
@@ -73,16 +72,19 @@ | @@ -73,16 +72,19 @@ | ||
73 | 72 | ||
74 | - (void)connectionDidFinishLoading:(NSURLConnection *)aConnection | 73 | - (void)connectionDidFinishLoading:(NSURLConnection *)aConnection |
75 | { | 74 | { |
76 | - UIImage *image = [[UIImage alloc] initWithData:imageData]; | ||
77 | - self.imageData = nil; | ||
78 | self.connection = nil; | 75 | self.connection = nil; |
79 | 76 | ||
80 | - if ([delegate respondsToSelector:@selector(imageDownloader:didFinishWithImage:)]) | 77 | + if ([delegate respondsToSelector:@selector(imageDownloaderDidFinish:)]) |
81 | { | 78 | { |
82 | - [delegate performSelector:@selector(imageDownloader:didFinishWithImage:) withObject:self withObject:image]; | 79 | + [delegate performSelector:@selector(imageDownloaderDidFinish:) withObject:self]; |
83 | } | 80 | } |
84 | 81 | ||
82 | + if ([delegate respondsToSelector:@selector(imageDownloader:didFinishWithImage:)]) | ||
83 | + { | ||
84 | + UIImage *image = [[UIImage alloc] initWithData:imageData]; | ||
85 | + [delegate performSelector:@selector(imageDownloader:didFinishWithImage:) withObject:self withObject:image]; | ||
85 | [image release]; | 86 | [image release]; |
87 | + } | ||
86 | } | 88 | } |
87 | 89 | ||
88 | - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error | 90 | - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error |
@@ -12,6 +12,7 @@ | @@ -12,6 +12,7 @@ | ||
12 | 12 | ||
13 | @optional | 13 | @optional |
14 | 14 | ||
15 | +- (void)imageDownloaderDidFinish:(SDWebImageDownloader *)downloader; | ||
15 | - (void)imageDownloader:(SDWebImageDownloader *)downloader didFinishWithImage:(UIImage *)image; | 16 | - (void)imageDownloader:(SDWebImageDownloader *)downloader didFinishWithImage:(UIImage *)image; |
16 | - (void)imageDownloader:(SDWebImageDownloader *)downloader didFailWithError:(NSError *)error; | 17 | - (void)imageDownloader:(SDWebImageDownloader *)downloader didFailWithError:(NSError *)error; |
17 | 18 |
@@ -120,7 +120,10 @@ static SDWebImageManager *instance; | @@ -120,7 +120,10 @@ static SDWebImageManager *instance; | ||
120 | if (image) | 120 | if (image) |
121 | { | 121 | { |
122 | // Store the image in the cache | 122 | // Store the image in the cache |
123 | - [[SDImageCache sharedImageCache] storeImage:image forKey:[downloader.url absoluteString]]; | 123 | + [[SDImageCache sharedImageCache] storeImage:image |
124 | + imageData:downloader.imageData | ||
125 | + forKey:[downloader.url absoluteString] | ||
126 | + toDisk:YES]; | ||
124 | } | 127 | } |
125 | else | 128 | else |
126 | { | 129 | { |
-
Please register or login to post a comment