10
10
#import " YYMemoryCache+SDAdditions.h"
11
11
#import " YYDiskCache+SDAdditions.h"
12
12
13
- static NSData * SDYYPluginCacheDataWithImageData (UIImage *image, NSData *imageData) {
14
- NSData *data = imageData;
15
- if (!data && [image conformsToProtocol: @protocol (SDAnimatedImage)]) {
16
- // If image is custom animated image class, prefer its original animated data
17
- data = [((id <SDAnimatedImage>)image) animatedImageData ];
13
+ static void SDYYPluginUnarchiveObject (NSData *data, UIImage *image) {
14
+ if (!data || !image) {
15
+ return ;
18
16
}
19
- if (!data && image) {
20
- // Check image's associated image format, may return .undefined
21
- SDImageFormat format = image.sd_imageFormat ;
22
- if (format == SDImageFormatUndefined) {
23
- // If image is animated, use GIF (APNG may be better, but has bugs before macOS 10.14)
24
- if (image.sd_isAnimated ) {
25
- format = SDImageFormatGIF;
26
- } else {
27
- // If we do not have any data to detect image format, check whether it contains alpha channel to use PNG or JPEG format
28
- if ([SDImageCoderHelper CGImageContainsAlpha: image.CGImage ]) {
29
- format = SDImageFormatPNG;
30
- } else {
31
- format = SDImageFormatJPEG;
32
- }
33
- }
17
+ // Check extended data
18
+ NSData *extendedData = [YYDiskCache getExtendedDataFromObject: data];
19
+ if (!extendedData) {
20
+ return ;
21
+ }
22
+ id extendedObject;
23
+ if (@available (iOS 11 , tvOS 11 , macOS 10.13 , watchOS 4 , *)) {
24
+ NSError *error;
25
+ NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc ] initForReadingFromData: extendedData error: &error];
26
+ unarchiver.requiresSecureCoding = NO ;
27
+ extendedObject = [unarchiver decodeTopLevelObjectForKey: NSKeyedArchiveRootObjectKey error: &error];
28
+ if (error) {
29
+ NSLog (@" NSKeyedUnarchiver unarchive failed with error: %@ " , error);
30
+ }
31
+ } else {
32
+ @try {
33
+ #pragma clang diagnostic push
34
+ #pragma clang diagnostic ignored "-Wdeprecated-declarations"
35
+ extendedObject = [NSKeyedUnarchiver unarchiveObjectWithData: extendedData];
36
+ #pragma clang diagnostic pop
37
+ } @catch (NSException *exception ) {
38
+ NSLog (@" NSKeyedUnarchiver unarchive failed with exception: %@ " , exception );
34
39
}
35
- data = [[SDImageCodersManager sharedManager ] encodedDataWithImage: image format: format options: nil ];
36
40
}
37
-
38
- return data;
41
+ image.sd_extendedObject = extendedObject;
42
+ }
43
+
44
+ static void SDYYPluginArchiveObject (NSData *data, UIImage *image) {
45
+ if (!data || !image) {
46
+ return ;
47
+ }
48
+ // Check extended data
49
+ id extendedObject = image.sd_extendedObject ;
50
+ if (![extendedObject conformsToProtocol: @protocol (NSCoding)]) {
51
+ return ;
52
+ }
53
+ NSData *extendedData;
54
+ if (@available (iOS 11 , tvOS 11 , macOS 10.13 , watchOS 4 , *)) {
55
+ NSError *error;
56
+ extendedData = [NSKeyedArchiver archivedDataWithRootObject: extendedObject requiringSecureCoding: NO error: &error];
57
+ if (error) {
58
+ NSLog (@" NSKeyedArchiver archive failed with error: %@ " , error);
59
+ }
60
+ } else {
61
+ @try {
62
+ #pragma clang diagnostic push
63
+ #pragma clang diagnostic ignored "-Wdeprecated-declarations"
64
+ extendedData = [NSKeyedArchiver archivedDataWithRootObject: extendedObject];
65
+ #pragma clang diagnostic pop
66
+ } @catch (NSException *exception ) {
67
+ NSLog (@" NSKeyedArchiver archive failed with exception: %@ " , exception );
68
+ }
69
+ }
70
+ if (extendedData) {
71
+ [YYDiskCache setExtendedData: extendedData toObject: data];
72
+ }
39
73
}
40
74
41
75
@implementation YYCache (SDAdditions)
@@ -68,8 +102,7 @@ @implementation YYCache (SDAdditions)
68
102
if (image) {
69
103
if (options & SDImageCacheDecodeFirstFrameOnly) {
70
104
// Ensure static image
71
- Class animatedImageClass = image.class ;
72
- if (image.sd_isAnimated || ([animatedImageClass isSubclassOfClass: [UIImage class ]] && [animatedImageClass conformsToProtocol: @protocol (SDAnimatedImage)])) {
105
+ if (image.sd_isAnimated ) {
73
106
#if SD_MAC
74
107
image = [[NSImage alloc ] initWithCGImage: image.CGImage scale: image.scale orientation: kCGImagePropertyOrientationUp ];
75
108
#else
@@ -95,176 +128,160 @@ @implementation YYCache (SDAdditions)
95
128
}
96
129
97
130
// Second check the disk cache...
131
+ SDCallbackQueue *queue = context[SDWebImageContextCallbackQueue];
98
132
NSOperation *operation = [NSOperation new ];
99
133
// Check whether we need to synchronously query disk
100
134
// 1. in-memory cache hit & memoryDataSync
101
135
// 2. in-memory cache miss & diskDataSync
102
136
BOOL shouldQueryDiskSync = ((image && options & SDImageCacheQueryMemoryDataSync) ||
103
137
(!image && options & SDImageCacheQueryDiskDataSync));
104
- void (^queryDiskBlock)( NSData * ) = ^( NSData *diskData) {
105
- if (operation. isCancelled ) {
106
- if (doneBlock ) {
107
- doneBlock ( nil , nil , SDImageCacheTypeNone) ;
138
+ NSData * (^queryDiskDataBlock)( void ) = ^NSData * {
139
+ @synchronized (operation) {
140
+ if (operation. isCancelled ) {
141
+ return nil ;
108
142
}
109
- return ;
110
143
}
111
144
112
- @autoreleasepool {
113
- UIImage *diskImage;
114
- if (image) {
115
- // the image is from in-memory cache, but need image data
116
- diskImage = image;
117
- } else if (diskData) {
118
- BOOL shouldCacheToMomery = YES ;
119
- if (context[SDWebImageContextStoreCacheType]) {
120
- SDImageCacheType cacheType = [context[SDWebImageContextStoreCacheType] integerValue ];
121
- shouldCacheToMomery = (cacheType == SDImageCacheTypeAll || cacheType == SDImageCacheTypeMemory);
122
- }
123
- // decode image data only if in-memory cache missed
124
- diskImage = SDImageCacheDecodeImageData (diskData, key, options, context);
125
- if (diskImage) {
126
- // Check extended data
127
- NSData *extendedData = [YYDiskCache getExtendedDataFromObject: diskData];
128
- if (extendedData) {
129
- id extendedObject;
130
- if (@available (iOS 11 , tvOS 11 , macOS 10.13 , watchOS 4 , *)) {
131
- NSError *error;
132
- NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc ] initForReadingFromData: extendedData error: &error];
133
- unarchiver.requiresSecureCoding = NO ;
134
- extendedObject = [unarchiver decodeTopLevelObjectForKey: NSKeyedArchiveRootObjectKey error: &error];
135
- if (error) {
136
- NSLog (@" NSKeyedUnarchiver unarchive failed with error: %@ " , error);
137
- }
138
- } else {
139
- @try {
140
- #pragma clang diagnostic push
141
- #pragma clang diagnostic ignored "-Wdeprecated-declarations"
142
- extendedObject = [NSKeyedUnarchiver unarchiveObjectWithData: extendedData];
143
- #pragma clang diagnostic pop
144
- } @catch (NSException *exception ) {
145
- NSLog (@" NSKeyedUnarchiver unarchive failed with exception: %@ " , exception );
146
- }
147
- }
148
- diskImage.sd_extendedObject = extendedObject;
149
- }
150
- }
151
- if (shouldCacheToMomery && diskImage) {
152
- NSUInteger cost = diskImage.sd_memoryCost ;
153
- [self .memoryCache setObject: diskImage forKey: key cost: cost];
154
- }
145
+ return [self .diskCache dataForKey: key];
146
+ };
147
+ UIImage* (^queryDiskImageBlock)(NSData *) = ^UIImage*(NSData * diskData) {
148
+ @synchronized (operation) {
149
+ if (operation.isCancelled ) {
150
+ return nil ;
155
151
}
156
-
157
- if (doneBlock) {
158
- if (shouldQueryDiskSync) {
159
- doneBlock (diskImage, diskData, SDImageCacheTypeDisk);
160
- } else {
161
- dispatch_async (dispatch_get_main_queue (), ^{
162
- doneBlock (diskImage, diskData, SDImageCacheTypeDisk);
163
- });
164
- }
152
+ }
153
+
154
+ UIImage *diskImage;
155
+ if (image) {
156
+ // the image is from in-memory cache, but need image data
157
+ diskImage = image;
158
+ } else if (diskData) {
159
+ BOOL shouldCacheToMomery = YES ;
160
+ if (context[SDWebImageContextStoreCacheType]) {
161
+ SDImageCacheType cacheType = [context[SDWebImageContextStoreCacheType] integerValue ];
162
+ shouldCacheToMomery = (cacheType == SDImageCacheTypeAll || cacheType == SDImageCacheTypeMemory);
163
+ }
164
+ CGSize thumbnailSize = CGSizeZero ;
165
+ NSValue *thumbnailSizeValue = context[SDWebImageContextImageThumbnailPixelSize];
166
+ if (thumbnailSizeValue != nil ) {
167
+ #if SD_MAC
168
+ thumbnailSize = thumbnailSizeValue.sizeValue ;
169
+ #else
170
+ thumbnailSize = thumbnailSizeValue.CGSizeValue ;
171
+ #endif
172
+ }
173
+ if (thumbnailSize.width > 0 && thumbnailSize.height > 0 ) {
174
+ // Query full size cache key which generate a thumbnail, should not write back to full size memory cache
175
+ shouldCacheToMomery = NO ;
176
+ }
177
+ // decode image data only if in-memory cache missed
178
+ diskImage = SDImageCacheDecodeImageData (diskData, key, options, context);
179
+ SDYYPluginUnarchiveObject (diskData, diskImage);
180
+ if (shouldCacheToMomery && diskImage) {
181
+ NSUInteger cost = diskImage.sd_memoryCost ;
182
+ [self .memoryCache setObject: diskImage forKey: key cost: cost];
165
183
}
166
184
}
185
+ return diskImage;
167
186
};
168
187
188
+ // YYCache has its own IO queue
169
189
if (shouldQueryDiskSync) {
170
- NSData *diskData = [self .diskCache dataForKey: key];
171
- queryDiskBlock (diskData);
190
+ NSData * diskData = queryDiskDataBlock ();
191
+ UIImage* diskImage = queryDiskImageBlock (diskData);
192
+ if (doneBlock) {
193
+ doneBlock (diskImage, diskData, SDImageCacheTypeDisk);
194
+ }
172
195
} else {
173
- // YYDiskCache's completion block is called in the global queue
174
- [self .diskCache objectForKey: key withBlock: ^(NSString * _Nonnull key, id <NSCoding > _Nullable object) {
175
- NSData *diskData = nil ;
176
- if ([(id <NSObject >)object isKindOfClass: [NSData class ]]) {
177
- diskData = (NSData *)object;
196
+ dispatch_async (dispatch_get_global_queue (DISPATCH_QUEUE_PRIORITY_HIGH, 0 ), ^{
197
+ NSData * diskData = queryDiskDataBlock ();
198
+ UIImage* diskImage = queryDiskImageBlock (diskData);
199
+ @synchronized (operation) {
200
+ if (operation.isCancelled ) {
201
+ return ;
202
+ }
178
203
}
179
- queryDiskBlock (diskData);
180
- }];
204
+ if (doneBlock) {
205
+ [(queue ?: SDCallbackQueue.mainQueue) async: ^{
206
+ // Dispatch from IO queue to main queue need time, user may call cancel during the dispatch timing
207
+ // This check is here to avoid double callback (one is from `SDImageCacheToken` in sync)
208
+ @synchronized (operation) {
209
+ if (operation.isCancelled ) {
210
+ return ;
211
+ }
212
+ }
213
+ doneBlock (diskImage, diskData, SDImageCacheTypeDisk);
214
+ }];
215
+ }
216
+ });
181
217
}
182
218
183
219
return operation;
184
220
}
185
221
186
- - (void )storeImageToDisk : (UIImage *)image imageData : (NSData *)imageData forKey : (NSString *)key completion : (SDWebImageNoParamsBlock)completionBlock {
187
- NSData *data = SDYYPluginCacheDataWithImageData (image, imageData);
188
- if (!data) {
189
- // SDImageCache does not remove object if `data` is nil
222
+ - (void )storeImage : (UIImage *)image imageData : (NSData *)imageData forKey : (NSString *)key cacheType : (SDImageCacheType)cacheType completion : (SDWebImageNoParamsBlock)completionBlock {
223
+ [self storeImage: image imageData: imageData forKey: key options: 0 context: nil cacheType: cacheType completion: completionBlock];
224
+ }
225
+
226
+ - (void )storeImage : (UIImage *)image imageData : (NSData *)imageData forKey : (NSString *)key options : (SDWebImageOptions)options context : (SDWebImageContext *)context cacheType : (SDImageCacheType)cacheType completion : (SDWebImageNoParamsBlock)completionBlock {
227
+ if ((!image && !imageData) || !key) {
190
228
if (completionBlock) {
191
- dispatch_async (dispatch_get_main_queue (), ^{
192
- completionBlock ();
193
- });
229
+ completionBlock ();
194
230
}
195
231
return ;
196
232
}
197
- if (image) {
198
- // Check extended data
199
- id extendedObject = image.sd_extendedObject ;
200
- if ([extendedObject conformsToProtocol: @protocol (NSCoding)]) {
201
- NSData *extendedData;
202
- if (@available (iOS 11 , tvOS 11 , macOS 10.13 , watchOS 4 , *)) {
203
- NSError *error;
204
- extendedData = [NSKeyedArchiver archivedDataWithRootObject: extendedObject requiringSecureCoding: NO error: &error];
205
- if (error) {
206
- NSLog (@" NSKeyedArchiver archive failed with error: %@ " , error);
207
- }
208
- } else {
209
- @try {
210
- #pragma clang diagnostic push
211
- #pragma clang diagnostic ignored "-Wdeprecated-declarations"
212
- extendedData = [NSKeyedArchiver archivedDataWithRootObject: extendedObject];
213
- #pragma clang diagnostic pop
214
- } @catch (NSException *exception ) {
215
- NSLog (@" NSKeyedArchiver archive failed with exception: %@ " , exception );
216
- }
217
- }
218
- if (extendedData) {
219
- [YYDiskCache setExtendedData: extendedData toObject: data];
220
- }
221
- }
233
+ BOOL toMemory = cacheType == SDImageCacheTypeMemory || cacheType == SDImageCacheTypeAll;
234
+ BOOL toDisk = cacheType == SDImageCacheTypeDisk || cacheType == SDImageCacheTypeAll;
235
+ // if memory cache is enabled
236
+ if (image && toMemory) {
237
+ NSUInteger cost = image.sd_memoryCost ;
238
+ [self .memoryCache setObject: image forKey: key cost: cost];
222
239
}
223
- [self .diskCache setObject: data forKey: key withBlock: ^{
240
+
241
+ if (!toDisk) {
224
242
if (completionBlock) {
225
- dispatch_async (dispatch_get_main_queue (), ^{
226
- completionBlock ();
227
- });
243
+ completionBlock ();
228
244
}
229
- }];
230
- }
231
-
232
- - (void )storeImage : (UIImage *)image imageData : (NSData *)imageData forKey : (NSString *)key cacheType : (SDImageCacheType)cacheType completion : (SDWebImageNoParamsBlock)completionBlock {
233
- switch (cacheType) {
234
- case SDImageCacheTypeNone: {
235
- if (completionBlock) {
236
- completionBlock ();
237
- }
238
- }
239
- break ;
240
- case SDImageCacheTypeMemory: {
241
- NSUInteger cost = image.sd_memoryCost ;
242
- [self .memoryCache setObject: image forKey: key cost: cost];
243
- if (completionBlock) {
244
- completionBlock ();
245
+ return ;
246
+ }
247
+ NSData *data = imageData;
248
+ if (!data && [image respondsToSelector: @selector (animatedImageData )]) {
249
+ // If image is custom animated image class, prefer its original animated data
250
+ data = [((id <SDAnimatedImage>)image) animatedImageData ];
251
+ }
252
+ SDCallbackQueue *queue = context[SDWebImageContextCallbackQueue];
253
+ if (!data && image) {
254
+ dispatch_async (dispatch_get_global_queue (DISPATCH_QUEUE_PRIORITY_HIGH, 0 ), ^{
255
+ // Check image's associated image format, may return .undefined
256
+ SDImageFormat format = image.sd_imageFormat ;
257
+ if (format == SDImageFormatUndefined) {
258
+ // If image is animated, use GIF (APNG may be better, but has bugs before macOS 10.14)
259
+ if (image.sd_isAnimated ) {
260
+ format = SDImageFormatGIF;
261
+ } else {
262
+ // If we do not have any data to detect image format, check whether it contains alpha channel to use PNG or JPEG format
263
+ format = [SDImageCoderHelper CGImageContainsAlpha: image.CGImage ] ? SDImageFormatPNG : SDImageFormatJPEG;
264
+ }
245
265
}
246
- }
247
- break ;
248
- case SDImageCacheTypeDisk: {
249
- dispatch_async (dispatch_get_global_queue (DISPATCH_QUEUE_PRIORITY_HIGH, 0 ), ^{
250
- [self storeImageToDisk: image imageData: imageData forKey: key completion: completionBlock];
251
- });
252
- }
253
- break ;
254
- case SDImageCacheTypeAll: {
255
- NSUInteger cost = image.sd_memoryCost ;
256
- [self .memoryCache setObject: image forKey: key cost: cost];
257
- dispatch_async (dispatch_get_global_queue (DISPATCH_QUEUE_PRIORITY_HIGH, 0 ), ^{
258
- [self storeImageToDisk: image imageData: imageData forKey: key completion: completionBlock];
259
- });
260
- }
261
- break ;
262
- default : {
266
+ NSData *data = [[SDImageCodersManager sharedManager ] encodedDataWithImage: image format: format options: context[SDWebImageContextImageEncodeOptions]];
267
+ SDYYPluginArchiveObject (data, image);
268
+ [self .diskCache setObject: data forKey: key withBlock: ^{
269
+ if (completionBlock) {
270
+ [(queue ?: SDCallbackQueue.mainQueue) async: ^{
271
+ completionBlock ();
272
+ }];
273
+ }
274
+ }];
275
+ });
276
+ } else {
277
+ SDYYPluginArchiveObject (data, image);
278
+ [self .diskCache setObject: data forKey: key withBlock: ^{
263
279
if (completionBlock) {
264
- completionBlock ();
280
+ [(queue ?: SDCallbackQueue.mainQueue) async: ^{
281
+ completionBlock ();
282
+ }];
265
283
}
266
- }
267
- break ;
284
+ }];
268
285
}
269
286
}
270
287
0 commit comments