Skip to content

Commit 2b352c3

Browse files
author
Olivier Poitrey
committed
Perform disk cache out operations asynchronousely in order to prevent from blocking the main runloop when a lot of cache queries are performed at the same time
1 parent dffff12 commit 2b352c3

File tree

6 files changed

+148
-42
lines changed

6 files changed

+148
-42
lines changed

SDImageCache.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,13 @@
77
*/
88

99
#import <Foundation/Foundation.h>
10+
#import "SDImageCacheDelegate.h"
1011

1112
@interface SDImageCache : NSObject
1213
{
1314
NSMutableDictionary *memCache, *storeDataQueue;
1415
NSString *diskCachePath;
15-
NSOperationQueue *cacheInQueue;
16+
NSOperationQueue *cacheInQueue, *cacheOutQueue;
1617
}
1718

1819
+ (SDImageCache *)sharedImageCache;
@@ -21,6 +22,8 @@
2122
- (void)storeImage:(UIImage *)image imageData:(NSData *)data forKey:(NSString *)key toDisk:(BOOL)toDisk;
2223
- (UIImage *)imageFromKey:(NSString *)key;
2324
- (UIImage *)imageFromKey:(NSString *)key fromDisk:(BOOL)fromDisk;
25+
- (void)queryDiskCacheForKey:(NSString *)key delegate:(id <SDImageCacheDelegate>)delegate userInfo:(NSDictionary *)info;
26+
2427
- (void)removeImageForKey:(NSString *)key;
2528
- (void)clearMemory;
2629
- (void)clearDisk;

SDImageCache.m

Lines changed: 82 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,9 @@ - (id)init
3939

4040
// Init the operation queue
4141
cacheInQueue = [[NSOperationQueue alloc] init];
42-
cacheInQueue.maxConcurrentOperationCount = 2;
42+
cacheInQueue.maxConcurrentOperationCount = 1;
43+
cacheOutQueue = [[NSOperationQueue alloc] init];
44+
cacheOutQueue.maxConcurrentOperationCount = 1;
4345

4446
// Subscribe to app events
4547
[[NSNotificationCenter defaultCenter] addObserver:self
@@ -134,6 +136,45 @@ - (void)storeKeyToDisk:(NSString *)key
134136
[fileManager release];
135137
}
136138

139+
- (void)notifyDelegate:(NSDictionary *)arguments
140+
{
141+
NSString *key = [arguments objectForKey:@"key"];
142+
id <SDImageCacheDelegate> delegate = [arguments objectForKey:@"delegate"];
143+
NSDictionary *info = [arguments objectForKey:@"userInfo"];
144+
UIImage *image = [arguments objectForKey:@"image"];
145+
146+
if (image)
147+
{
148+
[memCache setObject:image forKey:key];
149+
150+
if ([delegate respondsToSelector:@selector(imageCache:didFindImage:forKey:userInfo:)])
151+
{
152+
[delegate imageCache:self didFindImage:image forKey:key userInfo:info];
153+
}
154+
}
155+
else
156+
{
157+
if ([delegate respondsToSelector:@selector(imageCache:didNotFindImageForKey:userInfo:)])
158+
{
159+
[delegate imageCache:self didNotFindImageForKey:key userInfo:info];
160+
}
161+
}
162+
}
163+
164+
- (void)queryDiskCacheOperation:(NSDictionary *)arguments
165+
{
166+
NSString *key = [arguments objectForKey:@"key"];
167+
NSMutableDictionary *mutableArguments = [[arguments mutableCopy] autorelease];
168+
169+
UIImage *image = [[[UIImage alloc] initWithContentsOfFile:[self cachePathForKey:key]] autorelease];
170+
if (image)
171+
{
172+
[mutableArguments setObject:image forKey:@"image"];
173+
}
174+
175+
[self performSelectorOnMainThread:@selector(notifyDelegate:) withObject:mutableArguments waitUntilDone:NO];
176+
}
177+
137178
#pragma mark ImageCache
138179

139180
- (void)storeImage:(UIImage *)image imageData:(NSData *)data forKey:(NSString *)key toDisk:(BOOL)toDisk
@@ -154,7 +195,6 @@ - (void)storeImage:(UIImage *)image imageData:(NSData *)data forKey:(NSString *)
154195
{
155196
[storeDataQueue setObject:data forKey:key];
156197
[cacheInQueue addOperation:[[[NSInvocationOperation alloc] initWithTarget:self selector:@selector(storeKeyToDisk:) object:key] autorelease]];
157-
158198
}
159199
}
160200

@@ -185,17 +225,54 @@ - (UIImage *)imageFromKey:(NSString *)key fromDisk:(BOOL)fromDisk
185225

186226
if (!image && fromDisk)
187227
{
188-
image = [[UIImage alloc] initWithContentsOfFile:[self cachePathForKey:key]];
189-
if (image != nil)
228+
image = [[[UIImage alloc] initWithContentsOfFile:[self cachePathForKey:key]] autorelease];
229+
if (image)
190230
{
191231
[memCache setObject:image forKey:key];
192-
[image autorelease];
193232
}
194233
}
195234

196235
return image;
197236
}
198237

238+
- (void)queryDiskCacheForKey:(NSString *)key delegate:(id <SDImageCacheDelegate>)delegate userInfo:(NSDictionary *)info
239+
{
240+
if (!delegate)
241+
{
242+
return;
243+
}
244+
245+
if (!key)
246+
{
247+
if ([delegate respondsToSelector:@selector(imageCache:didNotFindImageForKey:userInfo:)])
248+
{
249+
[delegate imageCache:self didNotFindImageForKey:key userInfo:info];
250+
}
251+
return;
252+
}
253+
254+
// First check the in-memory cache...
255+
UIImage *image = [memCache objectForKey:key];
256+
if (image)
257+
{
258+
// ...notify delegate immediately, no need to go async
259+
if ([delegate respondsToSelector:@selector(imageCache:didFindImage:forKey:userInfo:)])
260+
{
261+
[delegate imageCache:self didFindImage:image forKey:key userInfo:info];
262+
}
263+
return;
264+
}
265+
266+
NSMutableDictionary *arguments = [NSMutableDictionary dictionaryWithCapacity:3];
267+
[arguments setObject:key forKey:@"key"];
268+
[arguments setObject:delegate forKey:@"delegate"];
269+
if (info)
270+
{
271+
[arguments setObject:info forKey:@"userInfo"];
272+
}
273+
[cacheOutQueue addOperation:[[[NSInvocationOperation alloc] initWithTarget:self selector:@selector(queryDiskCacheOperation:) object:arguments] autorelease]];
274+
}
275+
199276
- (void)removeImageForKey:(NSString *)key
200277
{
201278
if (key == nil)

SDImageCacheDelegate.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
//
2+
// SDImageCacheDelegate.h
3+
// Dailymotion
4+
//
5+
// Created by Olivier Poitrey on 16/09/10.
6+
// Copyright 2010 Dailymotion. All rights reserved.
7+
//
8+
9+
#import <UIKit/UIKit.h>
10+
11+
@class SDImageCache;
12+
13+
@protocol SDImageCacheDelegate <NSObject>
14+
15+
@optional
16+
- (void)imageCache:(SDImageCache *)imageCache didFindImage:(UIImage *)image forKey:(NSString *)key userInfo:(NSDictionary *)info;
17+
- (void)imageCache:(SDImageCache *)imageCache didNotFindImageForKey:(NSString *)key userInfo:(NSDictionary *)info;
18+
19+
@end

SDWebImageManager.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@
99
#import <UIKit/UIKit.h>
1010
#import "SDWebImageDownloaderDelegate.h"
1111
#import "SDWebImageManagerDelegate.h"
12+
#import "SDImageCacheDelegate.h"
1213

13-
@interface SDWebImageManager : NSObject <SDWebImageDownloaderDelegate>
14+
@interface SDWebImageManager : NSObject <SDWebImageDownloaderDelegate, SDImageCacheDelegate>
1415
{
1516
NSMutableArray *delegates;
1617
NSMutableArray *downloaders;

SDWebImageManager.m

Lines changed: 38 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -46,29 +46,24 @@ + (id)sharedManager
4646
return instance;
4747
}
4848

49+
/**
50+
* @deprecated
51+
*/
4952
- (UIImage *)imageWithURL:(NSURL *)url
5053
{
5154
return [[SDImageCache sharedImageCache] imageFromKey:[url absoluteString]];
5255
}
5356

5457
- (void)downloadWithURL:(NSURL *)url delegate:(id<SDWebImageManagerDelegate>)delegate
5558
{
56-
if (url == nil || [failedURLs containsObject:url])
59+
if (!url || !delegate || [failedURLs containsObject:url])
5760
{
5861
return;
5962
}
6063

61-
// Share the same downloader for identical URLs so we don't download the same URL several times
62-
SDWebImageDownloader *downloader = [downloaderForURL objectForKey:url];
63-
64-
if (!downloader)
65-
{
66-
downloader = [SDWebImageDownloader downloaderWithURL:url delegate:self];
67-
[downloaderForURL setObject:downloader forKey:url];
68-
}
69-
70-
[delegates addObject:delegate];
71-
[downloaders addObject:downloader];
64+
// Check the on-disk cache async so we don't block the main thread
65+
NSDictionary *info = [NSDictionary dictionaryWithObjectsAndKeys:delegate, @"delegate", url, @"url", nil];
66+
[[SDImageCache sharedImageCache] queryDiskCacheForKey:[url absoluteString] delegate:self userInfo:info];
7267
}
7368

7469
- (void)cancelForDelegate:(id<SDWebImageManagerDelegate>)delegate
@@ -95,6 +90,37 @@ - (void)cancelForDelegate:(id<SDWebImageManagerDelegate>)delegate
9590
[downloader release];
9691
}
9792

93+
#pragma mark SDImageCacheDelegate
94+
95+
- (void)imageCache:(SDImageCache *)imageCache didFindImage:(UIImage *)image forKey:(NSString *)key userInfo:(NSDictionary *)info
96+
{
97+
id<SDWebImageManagerDelegate> delegate = [info objectForKey:@"delegate"];
98+
if ([delegate respondsToSelector:@selector(webImageManager:didFinishWithImage:)])
99+
{
100+
[delegate performSelector:@selector(webImageManager:didFinishWithImage:) withObject:self withObject:image];
101+
}
102+
}
103+
104+
- (void)imageCache:(SDImageCache *)imageCache didNotFindImageForKey:(NSString *)key userInfo:(NSDictionary *)info
105+
{
106+
NSURL *url = [info objectForKey:@"url"];
107+
id<SDWebImageManagerDelegate> delegate = [info objectForKey:@"delegate"];
108+
109+
// Share the same downloader for identical URLs so we don't download the same URL several times
110+
SDWebImageDownloader *downloader = [downloaderForURL objectForKey:url];
111+
112+
if (!downloader)
113+
{
114+
downloader = [SDWebImageDownloader downloaderWithURL:url delegate:self];
115+
[downloaderForURL setObject:downloader forKey:url];
116+
}
117+
118+
[delegates addObject:delegate];
119+
[downloaders addObject:downloader];
120+
}
121+
122+
#pragma mark SDWebImageDownloaderDelegate
123+
98124
- (void)imageDownloader:(SDWebImageDownloader *)downloader didFinishWithImage:(UIImage *)image
99125
{
100126
[downloader retain];

UIImageView+WebCache.m

Lines changed: 3 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -23,31 +23,11 @@ - (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder
2323
// Remove in progress downloader from queue
2424
[manager cancelForDelegate:self];
2525

26-
UIImage *cachedImage = nil;
27-
if (url)
28-
{
29-
cachedImage = [manager imageWithURL:url];
30-
}
31-
else
32-
{
33-
self.image = placeholder;
34-
}
26+
self.image = placeholder;
3527

36-
if (cachedImage)
37-
{
38-
self.image = cachedImage;
39-
}
40-
else
28+
if (url)
4129
{
42-
if (placeholder)
43-
{
44-
self.image = placeholder;
45-
}
46-
47-
if (url)
48-
{
49-
[manager downloadWithURL:url delegate:self];
50-
}
30+
[manager downloadWithURL:url delegate:self];
5131
}
5232
}
5333

0 commit comments

Comments
 (0)