Merge pull request #3465 from dreampiggy/feat/callback_queue
Added context option `callbackQueue` and `SDCallbackQueue` wrapper for advanced user to control which queue to callback
This commit is contained in:
commit
05f7fb9b0d
|
@ -25,6 +25,8 @@
|
|||
320CAE172086F50500CFFC80 /* SDWebImageError.h in Headers */ = {isa = PBXBuildFile; fileRef = 320CAE132086F50500CFFC80 /* SDWebImageError.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
320CAE1B2086F50500CFFC80 /* SDWebImageError.m in Sources */ = {isa = PBXBuildFile; fileRef = 320CAE142086F50500CFFC80 /* SDWebImageError.m */; };
|
||||
320CAE1D2086F50500CFFC80 /* SDWebImageError.m in Sources */ = {isa = PBXBuildFile; fileRef = 320CAE142086F50500CFFC80 /* SDWebImageError.m */; };
|
||||
321117A9296573680001FC2C /* SDCallbackQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 321117A7296573680001FC2C /* SDCallbackQueue.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
321117AA296573680001FC2C /* SDCallbackQueue.m in Sources */ = {isa = PBXBuildFile; fileRef = 321117A8296573680001FC2C /* SDCallbackQueue.m */; };
|
||||
321B37832083290E00C0EA77 /* SDImageLoader.h in Headers */ = {isa = PBXBuildFile; fileRef = 321B377D2083290D00C0EA77 /* SDImageLoader.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
321B37872083290E00C0EA77 /* SDImageLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = 321B377E2083290D00C0EA77 /* SDImageLoader.m */; };
|
||||
321B37892083290E00C0EA77 /* SDImageLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = 321B377E2083290D00C0EA77 /* SDImageLoader.m */; };
|
||||
|
@ -67,6 +69,7 @@
|
|||
324DF4B6200A14DC008A84CC /* SDWebImageDefine.h in Headers */ = {isa = PBXBuildFile; fileRef = 324DF4B2200A14DC008A84CC /* SDWebImageDefine.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
324DF4BA200A14DC008A84CC /* SDWebImageDefine.m in Sources */ = {isa = PBXBuildFile; fileRef = 324DF4B3200A14DC008A84CC /* SDWebImageDefine.m */; };
|
||||
324DF4BC200A14DC008A84CC /* SDWebImageDefine.m in Sources */ = {isa = PBXBuildFile; fileRef = 324DF4B3200A14DC008A84CC /* SDWebImageDefine.m */; };
|
||||
325074F2296C546D00B730CF /* SDCallbackQueue.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 321117A7296573680001FC2C /* SDCallbackQueue.h */; };
|
||||
3250C9EE2355D9DA0093A896 /* SDWebImageDownloaderDecryptor.h in Headers */ = {isa = PBXBuildFile; fileRef = 3250C9EC2355D9DA0093A896 /* SDWebImageDownloaderDecryptor.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
3250C9EF2355D9DA0093A896 /* SDWebImageDownloaderDecryptor.m in Sources */ = {isa = PBXBuildFile; fileRef = 3250C9ED2355D9DA0093A896 /* SDWebImageDownloaderDecryptor.m */; };
|
||||
3250C9F02355D9DA0093A896 /* SDWebImageDownloaderDecryptor.m in Sources */ = {isa = PBXBuildFile; fileRef = 3250C9ED2355D9DA0093A896 /* SDWebImageDownloaderDecryptor.m */; };
|
||||
|
@ -317,6 +320,7 @@
|
|||
dstPath = include/SDWebImage;
|
||||
dstSubfolderSpec = 16;
|
||||
files = (
|
||||
325074F2296C546D00B730CF /* SDCallbackQueue.h in Copy Headers */,
|
||||
32D9EE4B24AF259B00EAFDF4 /* SDImageAWebPCoder.h in Copy Headers */,
|
||||
328E9DE523A61DD30051C893 /* SDGraphicsImageRenderer.h in Copy Headers */,
|
||||
325F7CCD2389467800AEDFCC /* UIImage+ExtendedCacheData.h in Copy Headers */,
|
||||
|
@ -387,6 +391,8 @@
|
|||
320224BA203979BA00E9F285 /* SDAnimatedImageRep.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SDAnimatedImageRep.m; path = Core/SDAnimatedImageRep.m; sourceTree = "<group>"; };
|
||||
320CAE132086F50500CFFC80 /* SDWebImageError.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SDWebImageError.h; path = Core/SDWebImageError.h; sourceTree = "<group>"; };
|
||||
320CAE142086F50500CFFC80 /* SDWebImageError.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SDWebImageError.m; path = Core/SDWebImageError.m; sourceTree = "<group>"; };
|
||||
321117A7296573680001FC2C /* SDCallbackQueue.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SDCallbackQueue.h; path = Core/SDCallbackQueue.h; sourceTree = "<group>"; };
|
||||
321117A8296573680001FC2C /* SDCallbackQueue.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SDCallbackQueue.m; path = Core/SDCallbackQueue.m; sourceTree = "<group>"; };
|
||||
321B377D2083290D00C0EA77 /* SDImageLoader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SDImageLoader.h; path = Core/SDImageLoader.h; sourceTree = "<group>"; };
|
||||
321B377E2083290D00C0EA77 /* SDImageLoader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SDImageLoader.m; path = Core/SDImageLoader.m; sourceTree = "<group>"; };
|
||||
321B377F2083290E00C0EA77 /* SDImageLoadersManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SDImageLoadersManager.h; path = Core/SDImageLoadersManager.h; sourceTree = "<group>"; };
|
||||
|
@ -872,6 +878,8 @@
|
|||
325312C7200F09910046BF1E /* SDWebImageTransition.m */,
|
||||
32C0FDDF2013426C001B8F2D /* SDWebImageIndicator.h */,
|
||||
32C0FDE02013426C001B8F2D /* SDWebImageIndicator.m */,
|
||||
321117A7296573680001FC2C /* SDCallbackQueue.h */,
|
||||
321117A8296573680001FC2C /* SDCallbackQueue.m */,
|
||||
);
|
||||
name = Utils;
|
||||
sourceTree = "<group>";
|
||||
|
@ -957,6 +965,7 @@
|
|||
3250C9EE2355D9DA0093A896 /* SDWebImageDownloaderDecryptor.h in Headers */,
|
||||
32F7C0862030719600873181 /* UIImage+Transform.h in Headers */,
|
||||
321E60C01F38E91700405457 /* UIImage+ForceDecode.h in Headers */,
|
||||
321117A9296573680001FC2C /* SDCallbackQueue.h in Headers */,
|
||||
329F1243223FAD3400B309FD /* SDInternalMacros.h in Headers */,
|
||||
80B6DF7F2142B43300BCB334 /* NSImage+Compatibility.h in Headers */,
|
||||
32C0FDE32013426C001B8F2D /* SDWebImageIndicator.h in Headers */,
|
||||
|
@ -1205,6 +1214,7 @@
|
|||
4A2CAE221AB4BB7000B6BC39 /* SDWebImageManager.m in Sources */,
|
||||
4A2CAE191AB4BB6400B6BC39 /* SDWebImageCompat.m in Sources */,
|
||||
325C460B22339426004CAE11 /* SDWeakProxy.m in Sources */,
|
||||
321117AA296573680001FC2C /* SDCallbackQueue.m in Sources */,
|
||||
321B37892083290E00C0EA77 /* SDImageLoader.m in Sources */,
|
||||
32484771201775F600AF9E5A /* SDAnimatedImage.m in Sources */,
|
||||
807A12301F89636300EC2A9B /* SDImageCodersManager.m in Sources */,
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* This file is part of the SDWebImage package.
|
||||
* (c) Olivier Poitrey <rs@dailymotion.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
|
||||
#import "SDWebImageCompat.h"
|
||||
|
||||
/// SDCallbackPolicy controls how we execute the block on the queue, like whether to use `dispatch_async/dispatch_sync`, check if current queue match target queue, or just invoke without any context.
|
||||
typedef NS_ENUM(NSUInteger, SDCallbackPolicy) {
|
||||
/// When the current queue is equal to callback queue, sync/async will just invoke `block` directly without dispatch. Else it use `dispatch_async`/`dispatch_sync` to dispatch block on queue. This is useful for UIKit rendering to ensure all blocks executed in the same runloop
|
||||
SDCallbackPolicySafeExecute = 0,
|
||||
/// Follow async/sync using the correspond `dispatch_async`/`dispatch_sync` to dispatch block on queue
|
||||
SDCallbackPolicyDispatch = 1,
|
||||
/// Ignore any async/sync and just directly invoke `block` in current queue (without `dispatch_async`/`dispatch_sync`)
|
||||
SDCallbackPolicyInvoke = 2
|
||||
};
|
||||
|
||||
/// SDCallbackQueue is a wrapper used to control how the completionBlock should perform on queues, used by our `Cache`/`Manager`/`Loader`.
|
||||
/// Useful when you call SDWebImage in non-main queue and want to avoid it callback into main queue, which may cause issue.
|
||||
@interface SDCallbackQueue : NSObject
|
||||
|
||||
/// The shared main queue. This is the default value, has the same effect when passing `nil` to `SDWebImageContextCallbackQueue`
|
||||
@property (nonnull, class, readonly) SDCallbackQueue *mainQueue;
|
||||
|
||||
/// The caller current queue. Using `dispatch_get_current_queue`. This is not a dynamic value and only keep the first call time queue.
|
||||
@property (nonnull, class, readonly) SDCallbackQueue *currentQueue;
|
||||
|
||||
/// The global concurrent queue (user-initiated QoS). Using `dispatch_get_global_queue`.
|
||||
@property (nonnull, class, readonly) SDCallbackQueue *globalQueue;
|
||||
|
||||
/// The current queue's callback policy, defaults to `SDCallbackPolicySafeExecute`, which behaves like the old macro `dispatch_main_async_safe`
|
||||
@property (assign, readwrite) SDCallbackPolicy policy;
|
||||
|
||||
- (nonnull instancetype)init NS_UNAVAILABLE;
|
||||
+ (nonnull instancetype)new NS_UNAVAILABLE;
|
||||
/// Create the callback queue with a GCD queue
|
||||
/// - Parameter queue: The GCD queue, should not be NULL
|
||||
- (nonnull instancetype)initWithDispatchQueue:(nonnull dispatch_queue_t)queue NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
#pragma mark - Execution Entry
|
||||
|
||||
/// Submits a block for execution and returns after that block finishes executing.
|
||||
/// - Parameter block: The block that contains the work to perform.
|
||||
- (void)sync:(nonnull NS_NOESCAPE dispatch_block_t)block;
|
||||
|
||||
/// Schedules a block asynchronously for execution.
|
||||
/// - Parameter block: The block that contains the work to perform.
|
||||
- (void)async:(nonnull NS_NOESCAPE dispatch_block_t)block;
|
||||
|
||||
@end
|
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
* This file is part of the SDWebImage package.
|
||||
* (c) Olivier Poitrey <rs@dailymotion.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
|
||||
#import "SDCallbackQueue.h"
|
||||
|
||||
@interface SDCallbackQueue ()
|
||||
|
||||
@property (nonatomic, strong, nonnull) dispatch_queue_t queue;
|
||||
|
||||
@end
|
||||
|
||||
static void * SDCallbackQueueKey = &SDCallbackQueueKey;
|
||||
static void SDReleaseBlock(void *context) {
|
||||
CFRelease(context);
|
||||
}
|
||||
|
||||
static void inline SDSafeExecute(dispatch_queue_t _Nonnull queue, dispatch_block_t _Nonnull block, BOOL async) {
|
||||
// Special handle for main queue label only (custom queue can have the same label)
|
||||
const char *label = dispatch_queue_get_label(queue);
|
||||
if (label && label == dispatch_queue_get_label(dispatch_get_main_queue())) {
|
||||
const char *currentLabel = dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL);
|
||||
if (label == currentLabel) {
|
||||
block();
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Check specific to detect queue equal
|
||||
void *specific = dispatch_queue_get_specific(queue, SDCallbackQueueKey);
|
||||
void *currentSpecific = dispatch_get_specific(SDCallbackQueueKey);
|
||||
if (specific && currentSpecific && CFGetTypeID(specific) == CFUUIDGetTypeID() && CFGetTypeID(currentSpecific) == CFUUIDGetTypeID() && CFEqual(specific, currentSpecific)) {
|
||||
block();
|
||||
} else {
|
||||
if (async) {
|
||||
dispatch_async(queue, block);
|
||||
} else {
|
||||
dispatch_sync(queue, block);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@implementation SDCallbackQueue
|
||||
|
||||
- (instancetype)initWithDispatchQueue:(dispatch_queue_t)queue {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
NSCParameterAssert(queue);
|
||||
CFUUIDRef UUID = CFUUIDCreate(kCFAllocatorDefault);
|
||||
dispatch_queue_set_specific(queue, SDCallbackQueueKey, (void *)UUID, SDReleaseBlock);
|
||||
_queue = queue;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
+ (SDCallbackQueue *)mainQueue {
|
||||
static dispatch_once_t onceToken;
|
||||
static SDCallbackQueue *queue;
|
||||
dispatch_once(&onceToken, ^{
|
||||
queue = [[SDCallbackQueue alloc] initWithDispatchQueue:dispatch_get_main_queue()];
|
||||
});
|
||||
return queue;
|
||||
}
|
||||
|
||||
+ (SDCallbackQueue *)currentQueue {
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
SDCallbackQueue *queue = [[SDCallbackQueue alloc] initWithDispatchQueue:dispatch_get_current_queue()];
|
||||
#pragma clang diagnostic pop
|
||||
return queue;
|
||||
}
|
||||
|
||||
+ (SDCallbackQueue *)globalQueue {
|
||||
SDCallbackQueue *queue = [[SDCallbackQueue alloc] initWithDispatchQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)];
|
||||
return queue;
|
||||
}
|
||||
|
||||
- (void)sync:(nonnull NS_NOESCAPE dispatch_block_t)block {
|
||||
switch (self.policy) {
|
||||
case SDCallbackPolicySafeExecute:
|
||||
SDSafeExecute(self.queue, block, NO);
|
||||
break;
|
||||
case SDCallbackPolicyDispatch:
|
||||
dispatch_sync(self.queue, block);
|
||||
break;
|
||||
case SDCallbackPolicyInvoke:
|
||||
block();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)async:(nonnull NS_NOESCAPE dispatch_block_t)block {
|
||||
switch (self.policy) {
|
||||
case SDCallbackPolicySafeExecute:
|
||||
SDSafeExecute(self.queue, block, YES);
|
||||
break;
|
||||
case SDCallbackPolicyDispatch:
|
||||
dispatch_async(self.queue, block);
|
||||
break;
|
||||
case SDCallbackPolicyInvoke:
|
||||
block();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
|
@ -225,6 +225,28 @@ typedef NS_OPTIONS(NSUInteger, SDImageCacheOptions) {
|
|||
toDisk:(BOOL)toDisk
|
||||
completion:(nullable SDWebImageNoParamsBlock)completionBlock;
|
||||
|
||||
/**
|
||||
* Asynchronously store an image into memory and disk cache at the given key.
|
||||
*
|
||||
* @param image The image to store
|
||||
* @param imageData The image data as returned by the server, this representation will be used for disk storage
|
||||
* instead of converting the given image object into a storable/compressed image format in order
|
||||
* to save quality and CPU
|
||||
* @param key The unique image cache key, usually it's image absolute URL
|
||||
* @param options A mask to specify options to use for this store
|
||||
* @param context The context options to use. Pass `.callbackQueue` to control callback queue
|
||||
* @param cacheType The image store op cache type
|
||||
* @param completionBlock A block executed after the operation is finished
|
||||
* @note If no image data is provided and encode to disk, we will try to detect the image format (using either `sd_imageFormat` or `SDAnimatedImage` protocol method) and animation status, to choose the best matched format, including GIF, JPEG or PNG.
|
||||
*/
|
||||
- (void)storeImage:(nullable UIImage *)image
|
||||
imageData:(nullable NSData *)imageData
|
||||
forKey:(nullable NSString *)key
|
||||
options:(SDWebImageOptions)options
|
||||
context:(nullable SDWebImageContext *)context
|
||||
cacheType:(SDImageCacheType)cacheType
|
||||
completion:(nullable SDWebImageNoParamsBlock)completionBlock;
|
||||
|
||||
/**
|
||||
* Synchronously store an image into memory cache at the given key.
|
||||
*
|
||||
|
|
|
@ -14,12 +14,14 @@
|
|||
#import "UIImage+MemoryCacheCost.h"
|
||||
#import "UIImage+Metadata.h"
|
||||
#import "UIImage+ExtendedCacheData.h"
|
||||
#import "SDCallbackQueue.h"
|
||||
|
||||
@interface SDImageCacheToken ()
|
||||
|
||||
@property (nonatomic, strong, nullable, readwrite) NSString *key;
|
||||
@property (nonatomic, assign, getter=isCancelled) BOOL cancelled;
|
||||
@property (nonatomic, copy, nullable) SDImageCacheQueryCompletionBlock doneBlock;
|
||||
@property (nonatomic, strong, nullable) SDCallbackQueue *callbackQueue;
|
||||
|
||||
@end
|
||||
|
||||
|
@ -43,9 +45,9 @@
|
|||
SDImageCacheQueryCompletionBlock doneBlock = self.doneBlock;
|
||||
self.doneBlock = nil;
|
||||
if (doneBlock) {
|
||||
dispatch_main_async_safe(^{
|
||||
[(self.callbackQueue ?: SDCallbackQueue.mainQueue) async:^{
|
||||
doneBlock(nil, nil, SDImageCacheTypeNone);
|
||||
});
|
||||
}];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -197,20 +199,20 @@ static NSString * _defaultDiskCacheDirectory;
|
|||
- (void)storeImage:(nullable UIImage *)image
|
||||
forKey:(nullable NSString *)key
|
||||
completion:(nullable SDWebImageNoParamsBlock)completionBlock {
|
||||
[self storeImage:image imageData:nil forKey:key toDisk:YES completion:completionBlock];
|
||||
[self storeImage:image imageData:nil forKey:key options:0 context:nil cacheType:SDImageCacheTypeAll completion:completionBlock];
|
||||
}
|
||||
|
||||
- (void)storeImage:(nullable UIImage *)image
|
||||
forKey:(nullable NSString *)key
|
||||
toDisk:(BOOL)toDisk
|
||||
completion:(nullable SDWebImageNoParamsBlock)completionBlock {
|
||||
[self storeImage:image imageData:nil forKey:key toDisk:toDisk completion:completionBlock];
|
||||
[self storeImage:image imageData:nil forKey:key options:0 context:nil cacheType:(toDisk ? SDImageCacheTypeAll : SDImageCacheTypeMemory) completion:completionBlock];
|
||||
}
|
||||
|
||||
- (void)storeImageData:(nullable NSData *)imageData
|
||||
forKey:(nullable NSString *)key
|
||||
completion:(nullable SDWebImageNoParamsBlock)completionBlock {
|
||||
[self storeImage:nil imageData:imageData forKey:key toDisk:YES completion:completionBlock];
|
||||
[self storeImage:nil imageData:imageData forKey:key options:0 context:nil cacheType:SDImageCacheTypeAll completion:completionBlock];
|
||||
}
|
||||
|
||||
- (void)storeImage:(nullable UIImage *)image
|
||||
|
@ -218,14 +220,15 @@ static NSString * _defaultDiskCacheDirectory;
|
|||
forKey:(nullable NSString *)key
|
||||
toDisk:(BOOL)toDisk
|
||||
completion:(nullable SDWebImageNoParamsBlock)completionBlock {
|
||||
return [self storeImage:image imageData:imageData forKey:key toMemory:YES toDisk:toDisk completion:completionBlock];
|
||||
return [self storeImage:image imageData:imageData forKey:key options:0 context:nil cacheType:(toDisk ? SDImageCacheTypeDisk : SDImageCacheTypeMemory) completion:completionBlock];
|
||||
}
|
||||
|
||||
- (void)storeImage:(nullable UIImage *)image
|
||||
imageData:(nullable NSData *)imageData
|
||||
forKey:(nullable NSString *)key
|
||||
toMemory:(BOOL)toMemory
|
||||
toDisk:(BOOL)toDisk
|
||||
options:(SDWebImageOptions)options
|
||||
context:(nullable SDWebImageContext *)context
|
||||
cacheType:(SDImageCacheType)cacheType
|
||||
completion:(nullable SDWebImageNoParamsBlock)completionBlock {
|
||||
if ((!image && !imageData) || !key) {
|
||||
if (completionBlock) {
|
||||
|
@ -233,6 +236,8 @@ static NSString * _defaultDiskCacheDirectory;
|
|||
}
|
||||
return;
|
||||
}
|
||||
BOOL toMemory = cacheType == SDImageCacheTypeMemory || cacheType == SDImageCacheTypeAll;
|
||||
BOOL toDisk = cacheType == SDImageCacheTypeDisk || cacheType == SDImageCacheTypeAll;
|
||||
// if memory cache is enabled
|
||||
if (image && toMemory && self.config.shouldCacheImagesInMemory) {
|
||||
NSUInteger cost = image.sd_memoryCost;
|
||||
|
@ -245,6 +250,7 @@ static NSString * _defaultDiskCacheDirectory;
|
|||
}
|
||||
return;
|
||||
}
|
||||
SDCallbackQueue *queue = context[SDWebImageContextCallbackQueue];
|
||||
dispatch_async(self.ioQueue, ^{
|
||||
@autoreleasepool {
|
||||
NSData *data = imageData;
|
||||
|
@ -271,9 +277,9 @@ static NSString * _defaultDiskCacheDirectory;
|
|||
}
|
||||
|
||||
if (completionBlock) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[(queue ?: SDCallbackQueue.mainQueue) async:^{
|
||||
completionBlock();
|
||||
});
|
||||
}];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -602,8 +608,10 @@ static NSString * _defaultDiskCacheDirectory;
|
|||
}
|
||||
|
||||
// Second check the disk cache...
|
||||
SDCallbackQueue *queue = context[SDWebImageContextCallbackQueue];
|
||||
SDImageCacheToken *operation = [[SDImageCacheToken alloc] initWithDoneBlock:doneBlock];
|
||||
operation.key = key;
|
||||
operation.callbackQueue = queue;
|
||||
// Check whether we need to synchronously query disk
|
||||
// 1. in-memory cache hit & memoryDataSync
|
||||
// 2. in-memory cache miss & diskDataSync
|
||||
|
@ -680,7 +688,7 @@ static NSString * _defaultDiskCacheDirectory;
|
|||
}
|
||||
}
|
||||
if (doneBlock) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[(queue ?: SDCallbackQueue.mainQueue) async:^{
|
||||
// Dispatch from IO queue to main queue need time, user may call cancel during the dispatch timing
|
||||
// This check is here to avoid double callback (one is from `SDImageCacheToken` in sync)
|
||||
@synchronized (operation) {
|
||||
|
@ -689,7 +697,7 @@ static NSString * _defaultDiskCacheDirectory;
|
|||
}
|
||||
}
|
||||
doneBlock(diskImage, diskData, SDImageCacheTypeDisk);
|
||||
});
|
||||
}];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -894,30 +902,7 @@ static NSString * _defaultDiskCacheDirectory;
|
|||
}
|
||||
|
||||
- (void)storeImage:(UIImage *)image imageData:(NSData *)imageData forKey:(nullable NSString *)key cacheType:(SDImageCacheType)cacheType completion:(nullable SDWebImageNoParamsBlock)completionBlock {
|
||||
switch (cacheType) {
|
||||
case SDImageCacheTypeNone: {
|
||||
[self storeImage:image imageData:imageData forKey:key toMemory:NO toDisk:NO completion:completionBlock];
|
||||
}
|
||||
break;
|
||||
case SDImageCacheTypeMemory: {
|
||||
[self storeImage:image imageData:imageData forKey:key toMemory:YES toDisk:NO completion:completionBlock];
|
||||
}
|
||||
break;
|
||||
case SDImageCacheTypeDisk: {
|
||||
[self storeImage:image imageData:imageData forKey:key toMemory:NO toDisk:YES completion:completionBlock];
|
||||
}
|
||||
break;
|
||||
case SDImageCacheTypeAll: {
|
||||
[self storeImage:image imageData:imageData forKey:key toMemory:YES toDisk:YES completion:completionBlock];
|
||||
}
|
||||
break;
|
||||
default: {
|
||||
if (completionBlock) {
|
||||
completionBlock();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
[self storeImage:image imageData:imageData forKey:key options:0 context:nil cacheType:cacheType completion:completionBlock];
|
||||
}
|
||||
|
||||
- (void)removeImageForKey:(NSString *)key cacheType:(SDImageCacheType)cacheType completion:(nullable SDWebImageNoParamsBlock)completionBlock {
|
||||
|
|
|
@ -81,22 +81,23 @@ FOUNDATION_EXPORT void SDSetDecodeOptionsToContext(SDWebImageMutableContext * _N
|
|||
|
||||
@param key The image cache key
|
||||
@param options A mask to specify options to use for this query
|
||||
@param context A context contains different options to perform specify changes or processes, see `SDWebImageContextOption`. This hold the extra objects which `options` enum can not hold.
|
||||
@param context A context contains different options to perform specify changes or processes, see `SDWebImageContextOption`. This hold the extra objects which `options` enum can not hold. Pass `.callbackQueue` to control callback queue
|
||||
@param completionBlock The completion block. Will not get called if the operation is cancelled
|
||||
@return The operation for this query
|
||||
*/
|
||||
- (nullable id<SDWebImageOperation>)queryImageForKey:(nullable NSString *)key
|
||||
options:(SDWebImageOptions)options
|
||||
context:(nullable SDWebImageContext *)context
|
||||
completion:(nullable SDImageCacheQueryCompletionBlock)completionBlock;
|
||||
completion:(nullable SDImageCacheQueryCompletionBlock)completionBlock API_DEPRECATED_WITH_REPLACEMENT("queryImageForKey:options:context:cacheType:completion:", macos(10.10, API_TO_BE_DEPRECATED), ios(8.0, API_TO_BE_DEPRECATED), tvos(9.0, API_TO_BE_DEPRECATED), watchos(2.0, API_TO_BE_DEPRECATED));
|
||||
|
||||
@optional
|
||||
/**
|
||||
Query the cached image from image cache for given key. The operation can be used to cancel the query.
|
||||
If image is cached in memory, completion is called synchronously, else asynchronously and depends on the options arg (See `SDWebImageQueryDiskSync`)
|
||||
|
||||
@param key The image cache key
|
||||
@param options A mask to specify options to use for this query
|
||||
@param context A context contains different options to perform specify changes or processes, see `SDWebImageContextOption`. This hold the extra objects which `options` enum can not hold.
|
||||
@param context A context contains different options to perform specify changes or processes, see `SDWebImageContextOption`. This hold the extra objects which `options` enum can not hold. Pass `.callbackQueue` to control callback queue
|
||||
@param cacheType Specify where to query the cache from. By default we use `.all`, which means both memory cache and disk cache. You can choose to query memory only or disk only as well. Pass `.none` is invalid and callback with nil immediately.
|
||||
@param completionBlock The completion block. Will not get called if the operation is cancelled
|
||||
@return The operation for this query
|
||||
|
@ -107,6 +108,7 @@ FOUNDATION_EXPORT void SDSetDecodeOptionsToContext(SDWebImageMutableContext * _N
|
|||
cacheType:(SDImageCacheType)cacheType
|
||||
completion:(nullable SDImageCacheQueryCompletionBlock)completionBlock;
|
||||
|
||||
@required
|
||||
/**
|
||||
Store the image into image cache for the given key. If cache type is memory only, completion is called synchronously, else asynchronously.
|
||||
|
||||
|
@ -120,8 +122,29 @@ FOUNDATION_EXPORT void SDSetDecodeOptionsToContext(SDWebImageMutableContext * _N
|
|||
imageData:(nullable NSData *)imageData
|
||||
forKey:(nullable NSString *)key
|
||||
cacheType:(SDImageCacheType)cacheType
|
||||
completion:(nullable SDWebImageNoParamsBlock)completionBlock API_DEPRECATED_WITH_REPLACEMENT("storeImage:imageData:forKey:options:context:cacheType:completion:", macos(10.10, API_TO_BE_DEPRECATED), ios(8.0, API_TO_BE_DEPRECATED), tvos(9.0, API_TO_BE_DEPRECATED), watchos(2.0, API_TO_BE_DEPRECATED));;
|
||||
|
||||
@optional
|
||||
/**
|
||||
Store the image into image cache for the given key. If cache type is memory only, completion is called synchronously, else asynchronously.
|
||||
|
||||
@param image The image to store
|
||||
@param imageData The image data to be used for disk storage
|
||||
@param key The image cache key
|
||||
@param options A mask to specify options to use for this store
|
||||
@param context The context options to use. Pass `.callbackQueue` to control callback queue
|
||||
@param cacheType The image store op cache type
|
||||
@param completionBlock A block executed after the operation is finished
|
||||
*/
|
||||
- (void)storeImage:(nullable UIImage *)image
|
||||
imageData:(nullable NSData *)imageData
|
||||
forKey:(nullable NSString *)key
|
||||
options:(SDWebImageOptions)options
|
||||
context:(nullable SDWebImageContext *)context
|
||||
cacheType:(SDImageCacheType)cacheType
|
||||
completion:(nullable SDWebImageNoParamsBlock)completionBlock;
|
||||
|
||||
@required
|
||||
/**
|
||||
Remove the image from image cache for the given key. If cache type is memory only, completion is called synchronously, else asynchronously.
|
||||
|
||||
|
|
|
@ -130,6 +130,10 @@
|
|||
}
|
||||
|
||||
- (void)storeImage:(UIImage *)image imageData:(NSData *)imageData forKey:(NSString *)key cacheType:(SDImageCacheType)cacheType completion:(SDWebImageNoParamsBlock)completionBlock {
|
||||
[self storeImage:image imageData:imageData forKey:key options:0 context:nil cacheType:cacheType completion:completionBlock];
|
||||
}
|
||||
|
||||
- (void)storeImage:(UIImage *)image imageData:(NSData *)imageData forKey:(NSString *)key options:(SDWebImageOptions)options context:(SDWebImageContext *)context cacheType:(SDImageCacheType)cacheType completion:(SDWebImageNoParamsBlock)completionBlock {
|
||||
if (!key) {
|
||||
return;
|
||||
}
|
||||
|
@ -138,28 +142,28 @@
|
|||
if (count == 0) {
|
||||
return;
|
||||
} else if (count == 1) {
|
||||
[caches.firstObject storeImage:image imageData:imageData forKey:key cacheType:cacheType completion:completionBlock];
|
||||
[caches.firstObject storeImage:image imageData:imageData forKey:key options:options context:context cacheType:cacheType completion:completionBlock];
|
||||
return;
|
||||
}
|
||||
switch (self.storeOperationPolicy) {
|
||||
case SDImageCachesManagerOperationPolicyHighestOnly: {
|
||||
id<SDImageCache> cache = caches.lastObject;
|
||||
[cache storeImage:image imageData:imageData forKey:key cacheType:cacheType completion:completionBlock];
|
||||
[cache storeImage:image imageData:imageData forKey:key options:options context:context cacheType:cacheType completion:completionBlock];
|
||||
}
|
||||
break;
|
||||
case SDImageCachesManagerOperationPolicyLowestOnly: {
|
||||
id<SDImageCache> cache = caches.firstObject;
|
||||
[cache storeImage:image imageData:imageData forKey:key cacheType:cacheType completion:completionBlock];
|
||||
[cache storeImage:image imageData:imageData forKey:key options:options context:context cacheType:cacheType completion:completionBlock];
|
||||
}
|
||||
break;
|
||||
case SDImageCachesManagerOperationPolicyConcurrent: {
|
||||
SDImageCachesManagerOperation *operation = [SDImageCachesManagerOperation new];
|
||||
[operation beginWithTotalCount:caches.count];
|
||||
[self concurrentStoreImage:image imageData:imageData forKey:key cacheType:cacheType completion:completionBlock enumerator:caches.reverseObjectEnumerator operation:operation];
|
||||
[self concurrentStoreImage:image imageData:imageData forKey:key options:options context:context cacheType:cacheType completion:completionBlock enumerator:caches.reverseObjectEnumerator operation:operation];
|
||||
}
|
||||
break;
|
||||
case SDImageCachesManagerOperationPolicySerial: {
|
||||
[self serialStoreImage:image imageData:imageData forKey:key cacheType:cacheType completion:completionBlock enumerator:caches.reverseObjectEnumerator];
|
||||
[self serialStoreImage:image imageData:imageData forKey:key options:options context:context cacheType:cacheType completion:completionBlock enumerator:caches.reverseObjectEnumerator];
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
@ -315,11 +319,11 @@
|
|||
}
|
||||
}
|
||||
|
||||
- (void)concurrentStoreImage:(UIImage *)image imageData:(NSData *)imageData forKey:(NSString *)key cacheType:(SDImageCacheType)cacheType completion:(SDWebImageNoParamsBlock)completionBlock enumerator:(NSEnumerator<id<SDImageCache>> *)enumerator operation:(SDImageCachesManagerOperation *)operation {
|
||||
- (void)concurrentStoreImage:(UIImage *)image imageData:(NSData *)imageData forKey:(NSString *)key options:(SDWebImageOptions)options context:(SDWebImageContext *)context cacheType:(SDImageCacheType)cacheType completion:(SDWebImageNoParamsBlock)completionBlock enumerator:(NSEnumerator<id<SDImageCache>> *)enumerator operation:(SDImageCachesManagerOperation *)operation {
|
||||
NSParameterAssert(enumerator);
|
||||
NSParameterAssert(operation);
|
||||
for (id<SDImageCache> cache in enumerator) {
|
||||
[cache storeImage:image imageData:imageData forKey:key cacheType:cacheType completion:^{
|
||||
[cache storeImage:image imageData:imageData forKey:key options:options context:context cacheType:cacheType completion:^{
|
||||
if (operation.isCancelled) {
|
||||
// Cancelled
|
||||
return;
|
||||
|
@ -462,7 +466,7 @@
|
|||
}];
|
||||
}
|
||||
|
||||
- (void)serialStoreImage:(UIImage *)image imageData:(NSData *)imageData forKey:(NSString *)key cacheType:(SDImageCacheType)cacheType completion:(SDWebImageNoParamsBlock)completionBlock enumerator:(NSEnumerator<id<SDImageCache>> *)enumerator {
|
||||
- (void)serialStoreImage:(UIImage *)image imageData:(NSData *)imageData forKey:(NSString *)key options:(SDWebImageOptions)options context:(SDWebImageContext *)context cacheType:(SDImageCacheType)cacheType completion:(SDWebImageNoParamsBlock)completionBlock enumerator:(NSEnumerator<id<SDImageCache>> *)enumerator {
|
||||
NSParameterAssert(enumerator);
|
||||
id<SDImageCache> cache = enumerator.nextObject;
|
||||
if (!cache) {
|
||||
|
@ -473,10 +477,10 @@
|
|||
return;
|
||||
}
|
||||
@weakify(self);
|
||||
[cache storeImage:image imageData:imageData forKey:key cacheType:cacheType completion:^{
|
||||
[cache storeImage:image imageData:imageData forKey:key options:options context:context cacheType:cacheType completion:^{
|
||||
@strongify(self);
|
||||
// Next
|
||||
[self serialStoreImage:image imageData:imageData forKey:key cacheType:cacheType completion:completionBlock enumerator:enumerator];
|
||||
[self serialStoreImage:image imageData:imageData forKey:key options:options context:context cacheType:cacheType completion:completionBlock enumerator:enumerator];
|
||||
}];
|
||||
}
|
||||
|
||||
|
|
|
@ -81,7 +81,7 @@ FOUNDATION_EXPORT void SDImageLoaderSetProgressiveCoder(id<SDWebImageOperation>
|
|||
@param url The image URL to be loaded.
|
||||
@return YES to continue download, NO to stop download.
|
||||
*/
|
||||
- (BOOL)canRequestImageForURL:(nullable NSURL *)url API_DEPRECATED("Use canRequestImageForURL:options:context: instead", macos(10.10, API_TO_BE_DEPRECATED), ios(8.0, API_TO_BE_DEPRECATED), tvos(9.0, API_TO_BE_DEPRECATED), watchos(2.0, API_TO_BE_DEPRECATED));
|
||||
- (BOOL)canRequestImageForURL:(nullable NSURL *)url API_DEPRECATED_WITH_REPLACEMENT("canRequestImageForURL:options:context:", macos(10.10, API_TO_BE_DEPRECATED), ios(8.0, API_TO_BE_DEPRECATED), tvos(9.0, API_TO_BE_DEPRECATED), watchos(2.0, API_TO_BE_DEPRECATED));
|
||||
|
||||
@optional
|
||||
/**
|
||||
|
@ -125,7 +125,7 @@ FOUNDATION_EXPORT void SDImageLoaderSetProgressiveCoder(id<SDWebImageOperation>
|
|||
@return Whether to block this url or not. Return YES to mark this URL as failed.
|
||||
*/
|
||||
- (BOOL)shouldBlockFailedURLWithURL:(nonnull NSURL *)url
|
||||
error:(nonnull NSError *)error API_DEPRECATED("Use shouldBlockFailedURLWithURL:error:options:context: instead", macos(10.10, API_TO_BE_DEPRECATED), ios(8.0, API_TO_BE_DEPRECATED), tvos(9.0, API_TO_BE_DEPRECATED), watchos(2.0, API_TO_BE_DEPRECATED));
|
||||
error:(nonnull NSError *)error API_DEPRECATED_WITH_REPLACEMENT("shouldBlockFailedURLWithURL:error:options:context:", macos(10.10, API_TO_BE_DEPRECATED), ios(8.0, API_TO_BE_DEPRECATED), tvos(9.0, API_TO_BE_DEPRECATED), watchos(2.0, API_TO_BE_DEPRECATED));
|
||||
|
||||
@optional
|
||||
/**
|
||||
|
|
|
@ -221,6 +221,15 @@ FOUNDATION_EXPORT SDWebImageContextOption _Nonnull const SDWebImageContextSetIma
|
|||
*/
|
||||
FOUNDATION_EXPORT SDWebImageContextOption _Nonnull const SDWebImageContextCustomManager API_DEPRECATED("Use individual context option like .imageCache, .imageLoader and .imageTransformer instead", macos(10.10, API_TO_BE_DEPRECATED), ios(8.0, API_TO_BE_DEPRECATED), tvos(9.0, API_TO_BE_DEPRECATED), watchos(2.0, API_TO_BE_DEPRECATED));
|
||||
|
||||
/**
|
||||
A `SDCallbackQueue` instance which controls the `Cache`/`Manager`/`Loader`'s callback queue for their completionBlock.
|
||||
This is useful for user who call these 3 components in non-main queue and want to avoid callback in main queue.
|
||||
@note For UI callback (`sd_setImageWithURL`), we will still use main queue to dispatch, means if you specify a global queue, it will enqueue from the global queue to main queue.
|
||||
@note This does not effect the components' working queue (for example, `Cache` still query disk on internal ioQueue, `Loader` still do network on URLSessionConfiguration.delegateQueue), change those config if you need.
|
||||
Defaults to nil. Which means main queue.
|
||||
*/
|
||||
FOUNDATION_EXPORT SDWebImageContextOption _Nonnull const SDWebImageContextCallbackQueue;
|
||||
|
||||
/**
|
||||
A id<SDImageCache> instance which conforms to `SDImageCache` protocol. It's used to override the image manager's cache during the image loading pipeline.
|
||||
In other word, if you just want to specify a custom cache during image loading, you don't need to re-create a dummy SDWebImageManager instance with the cache. If not provided, use the image manager's cache (id<SDImageCache>)
|
||||
|
|
|
@ -127,6 +127,7 @@ inline UIImage * _Nullable SDScaledImageForScaleFactor(CGFloat scale, UIImage *
|
|||
|
||||
SDWebImageContextOption const SDWebImageContextSetImageOperationKey = @"setImageOperationKey";
|
||||
SDWebImageContextOption const SDWebImageContextCustomManager = @"customManager";
|
||||
SDWebImageContextOption const SDWebImageContextCallbackQueue = @"callbackQueue";
|
||||
SDWebImageContextOption const SDWebImageContextImageCache = @"imageCache";
|
||||
SDWebImageContextOption const SDWebImageContextImageLoader = @"imageLoader";
|
||||
SDWebImageContextOption const SDWebImageContextImageCoder = @"imageCoder";
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#import "SDWebImageDownloaderResponseModifier.h"
|
||||
#import "SDWebImageDownloaderDecryptor.h"
|
||||
#import "SDImageCacheDefine.h"
|
||||
#import "SDCallbackQueue.h"
|
||||
|
||||
// A handler to represent individual request
|
||||
@interface SDWebImageDownloaderOperationToken : NSObject
|
||||
|
@ -157,12 +158,7 @@
|
|||
@synchronized (self) {
|
||||
[self.callbackTokens removeObjectIdenticalTo:token];
|
||||
}
|
||||
SDWebImageDownloaderCompletedBlock completedBlock = ((SDWebImageDownloaderOperationToken *)token).completedBlock;
|
||||
if (completedBlock) {
|
||||
dispatch_main_async_safe(^{
|
||||
completedBlock(nil, nil, [NSError errorWithDomain:SDWebImageErrorDomain code:SDWebImageErrorCancelled userInfo:@{NSLocalizedDescriptionKey : @"Operation cancelled by user during sending the request"}], YES);
|
||||
});
|
||||
}
|
||||
[self callCompletionBlockWithToken:token image:nil imageData:nil error:[NSError errorWithDomain:SDWebImageErrorDomain code:SDWebImageErrorCancelled userInfo:@{NSLocalizedDescriptionKey : @"Operation cancelled by user during sending the request"}] finished:YES];
|
||||
}
|
||||
return shouldCancel;
|
||||
}
|
||||
|
@ -689,9 +685,9 @@ didReceiveResponse:(NSURLResponse *)response
|
|||
}
|
||||
|
||||
- (void)callCompletionBlocksWithImage:(nullable UIImage *)image
|
||||
imageData:(nullable NSData *)imageData
|
||||
error:(nullable NSError *)error
|
||||
finished:(BOOL)finished {
|
||||
imageData:(nullable NSData *)imageData
|
||||
error:(nullable NSError *)error
|
||||
finished:(BOOL)finished {
|
||||
NSArray<SDWebImageDownloaderOperationToken *> *tokens;
|
||||
@synchronized (self) {
|
||||
tokens = [self.callbackTokens copy];
|
||||
|
@ -699,9 +695,10 @@ didReceiveResponse:(NSURLResponse *)response
|
|||
for (SDWebImageDownloaderOperationToken *token in tokens) {
|
||||
SDWebImageDownloaderCompletedBlock completedBlock = token.completedBlock;
|
||||
if (completedBlock) {
|
||||
dispatch_main_async_safe(^{
|
||||
SDCallbackQueue *queue = self.context[SDWebImageContextCallbackQueue];
|
||||
[(queue ?: SDCallbackQueue.mainQueue) async:^{
|
||||
completedBlock(image, imageData, error, finished);
|
||||
});
|
||||
}];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -713,9 +710,10 @@ didReceiveResponse:(NSURLResponse *)response
|
|||
finished:(BOOL)finished {
|
||||
SDWebImageDownloaderCompletedBlock completedBlock = token.completedBlock;
|
||||
if (completedBlock) {
|
||||
dispatch_main_async_safe(^{
|
||||
SDCallbackQueue *queue = self.context[SDWebImageContextCallbackQueue];
|
||||
[(queue ?: SDCallbackQueue.mainQueue) async:^{
|
||||
completedBlock(image, imageData, error, finished);
|
||||
});
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#import "SDAssociatedObject.h"
|
||||
#import "SDWebImageError.h"
|
||||
#import "SDInternalMacros.h"
|
||||
#import "SDCallbackQueue.h"
|
||||
|
||||
static id<SDImageCache> _defaultImageCache;
|
||||
static id<SDImageLoader> _defaultImageLoader;
|
||||
|
@ -213,11 +214,14 @@ static id<SDImageLoader> _defaultImageLoader;
|
|||
isFailedUrl = [self.failedURLs containsObject:url];
|
||||
SD_UNLOCK(_failedURLsLock);
|
||||
}
|
||||
|
||||
// Preprocess the options and context arg to decide the final the result for manager
|
||||
SDWebImageOptionsResult *result = [self processedResultForURL:url options:options context:context];
|
||||
|
||||
if (url.absoluteString.length == 0 || (!(options & SDWebImageRetryFailed) && isFailedUrl)) {
|
||||
NSString *description = isFailedUrl ? @"Image url is blacklisted" : @"Image url is nil";
|
||||
NSInteger code = isFailedUrl ? SDWebImageErrorBlackListed : SDWebImageErrorInvalidURL;
|
||||
[self callCompletionBlockForOperation:operation completion:completedBlock error:[NSError errorWithDomain:SDWebImageErrorDomain code:code userInfo:@{NSLocalizedDescriptionKey : description}] url:url];
|
||||
[self callCompletionBlockForOperation:operation completion:completedBlock error:[NSError errorWithDomain:SDWebImageErrorDomain code:code userInfo:@{NSLocalizedDescriptionKey : description}] queue:result.context[SDWebImageContextCallbackQueue] url:url];
|
||||
return operation;
|
||||
}
|
||||
|
||||
|
@ -225,9 +229,6 @@ static id<SDImageLoader> _defaultImageLoader;
|
|||
[self.runningOperations addObject:operation];
|
||||
SD_UNLOCK(_runningOperationsLock);
|
||||
|
||||
// Preprocess the options and context arg to decide the final the result for manager
|
||||
SDWebImageOptionsResult *result = [self processedResultForURL:url options:options context:context];
|
||||
|
||||
// Start the entry to load image from cache, the longest steps are below
|
||||
// Steps without transformer:
|
||||
// 1. query image from cache, miss
|
||||
|
@ -306,7 +307,7 @@ static id<SDImageLoader> _defaultImageLoader;
|
|||
@strongify(operation);
|
||||
if (!operation || operation.isCancelled) {
|
||||
// Image combined operation cancelled by user
|
||||
[self callCompletionBlockForOperation:operation completion:completedBlock error:[NSError errorWithDomain:SDWebImageErrorDomain code:SDWebImageErrorCancelled userInfo:@{NSLocalizedDescriptionKey : @"Operation cancelled by user during querying the cache"}] url:url];
|
||||
[self callCompletionBlockForOperation:operation completion:completedBlock error:[NSError errorWithDomain:SDWebImageErrorDomain code:SDWebImageErrorCancelled userInfo:@{NSLocalizedDescriptionKey : @"Operation cancelled by user during querying the cache"}] queue:context[SDWebImageContextCallbackQueue] url:url];
|
||||
[self safelyRemoveOperationFromRunning:operation];
|
||||
return;
|
||||
} else if (!cachedImage) {
|
||||
|
@ -360,7 +361,7 @@ static id<SDImageLoader> _defaultImageLoader;
|
|||
@strongify(operation);
|
||||
if (!operation || operation.isCancelled) {
|
||||
// Image combined operation cancelled by user
|
||||
[self callCompletionBlockForOperation:operation completion:completedBlock error:[NSError errorWithDomain:SDWebImageErrorDomain code:SDWebImageErrorCancelled userInfo:@{NSLocalizedDescriptionKey : @"Operation cancelled by user during querying the cache"}] url:url];
|
||||
[self callCompletionBlockForOperation:operation completion:completedBlock error:[NSError errorWithDomain:SDWebImageErrorDomain code:SDWebImageErrorCancelled userInfo:@{NSLocalizedDescriptionKey : @"Operation cancelled by user during querying the cache"}] queue:context[SDWebImageContextCallbackQueue] url:url];
|
||||
[self safelyRemoveOperationFromRunning:operation];
|
||||
return;
|
||||
} else if (!cachedImage) {
|
||||
|
@ -414,7 +415,7 @@ static id<SDImageLoader> _defaultImageLoader;
|
|||
if (cachedImage && options & SDWebImageRefreshCached) {
|
||||
// If image was found in the cache but SDWebImageRefreshCached is provided, notify about the cached image
|
||||
// AND try to re-download it in order to let a chance to NSURLCache to refresh it from server.
|
||||
[self callCompletionBlockForOperation:operation completion:completedBlock image:cachedImage data:cachedData error:nil cacheType:cacheType finished:YES url:url];
|
||||
[self callCompletionBlockForOperation:operation completion:completedBlock image:cachedImage data:cachedData error:nil cacheType:cacheType finished:YES queue:context[SDWebImageContextCallbackQueue] url:url];
|
||||
// Pass the cached image to the image loader. The image loader should check whether the remote image is equal to the cached image.
|
||||
SDWebImageMutableContext *mutableContext;
|
||||
if (context) {
|
||||
|
@ -431,14 +432,14 @@ static id<SDImageLoader> _defaultImageLoader;
|
|||
@strongify(operation);
|
||||
if (!operation || operation.isCancelled) {
|
||||
// Image combined operation cancelled by user
|
||||
[self callCompletionBlockForOperation:operation completion:completedBlock error:[NSError errorWithDomain:SDWebImageErrorDomain code:SDWebImageErrorCancelled userInfo:@{NSLocalizedDescriptionKey : @"Operation cancelled by user during sending the request"}] url:url];
|
||||
[self callCompletionBlockForOperation:operation completion:completedBlock error:[NSError errorWithDomain:SDWebImageErrorDomain code:SDWebImageErrorCancelled userInfo:@{NSLocalizedDescriptionKey : @"Operation cancelled by user during sending the request"}] queue:context[SDWebImageContextCallbackQueue] url:url];
|
||||
} else if (cachedImage && options & SDWebImageRefreshCached && [error.domain isEqualToString:SDWebImageErrorDomain] && error.code == SDWebImageErrorCacheNotModified) {
|
||||
// Image refresh hit the NSURLCache cache, do not call the completion block
|
||||
} else if ([error.domain isEqualToString:SDWebImageErrorDomain] && error.code == SDWebImageErrorCancelled) {
|
||||
// Download operation cancelled by user before sending the request, don't block failed URL
|
||||
[self callCompletionBlockForOperation:operation completion:completedBlock error:error url:url];
|
||||
[self callCompletionBlockForOperation:operation completion:completedBlock error:error queue:context[SDWebImageContextCallbackQueue] url:url];
|
||||
} else if (error) {
|
||||
[self callCompletionBlockForOperation:operation completion:completedBlock error:error url:url];
|
||||
[self callCompletionBlockForOperation:operation completion:completedBlock error:error queue:context[SDWebImageContextCallbackQueue] url:url];
|
||||
BOOL shouldBlockFailedURL = [self shouldBlockFailedURLWithURL:url error:error options:options context:context];
|
||||
|
||||
if (shouldBlockFailedURL) {
|
||||
|
@ -461,11 +462,11 @@ static id<SDImageLoader> _defaultImageLoader;
|
|||
}
|
||||
}];
|
||||
} else if (cachedImage) {
|
||||
[self callCompletionBlockForOperation:operation completion:completedBlock image:cachedImage data:cachedData error:nil cacheType:cacheType finished:YES url:url];
|
||||
[self callCompletionBlockForOperation:operation completion:completedBlock image:cachedImage data:cachedData error:nil cacheType:cacheType finished:YES queue:context[SDWebImageContextCallbackQueue] url:url];
|
||||
[self safelyRemoveOperationFromRunning:operation];
|
||||
} else {
|
||||
// Image not in cache and download disallowed by delegate
|
||||
[self callCompletionBlockForOperation:operation completion:completedBlock image:nil data:nil error:nil cacheType:SDImageCacheTypeNone finished:YES url:url];
|
||||
[self callCompletionBlockForOperation:operation completion:completedBlock image:nil data:nil error:nil cacheType:SDImageCacheTypeNone finished:YES queue:context[SDWebImageContextCallbackQueue] url:url];
|
||||
[self safelyRemoveOperationFromRunning:operation];
|
||||
}
|
||||
}
|
||||
|
@ -538,7 +539,6 @@ static id<SDImageLoader> _defaultImageLoader;
|
|||
imageCache = self.imageCache;
|
||||
}
|
||||
}
|
||||
BOOL waitStoreCache = SD_OPTIONS_CONTAINS(options, SDWebImageWaitStoreCache);
|
||||
// the original store image cache type
|
||||
SDImageCacheType originalStoreCacheType = SDImageCacheTypeDisk;
|
||||
if (context[SDWebImageContextOriginalStoreCacheType]) {
|
||||
|
@ -560,7 +560,7 @@ static id<SDImageLoader> _defaultImageLoader;
|
|||
@autoreleasepool {
|
||||
NSData *newOriginalData = [cacheSerializer cacheDataWithImage:originalImage originalData:originalData imageURL:url];
|
||||
// Store original image and data
|
||||
[self storeImage:originalImage imageData:newOriginalData forKey:key imageCache:imageCache cacheType:originalStoreCacheType finished:finished waitStoreCache:waitStoreCache completion:^{
|
||||
[self storeImage:originalImage imageData:newOriginalData forKey:key options:options context:context imageCache:imageCache cacheType:originalStoreCacheType finished:finished completion:^{
|
||||
// Continue store cache process, transformed data is nil
|
||||
[self callStoreCacheProcessForOperation:operation url:url options:options context:context image:cacheImage data:cacheData cacheType:cacheType finished:finished completed:completedBlock];
|
||||
}];
|
||||
|
@ -568,7 +568,7 @@ static id<SDImageLoader> _defaultImageLoader;
|
|||
});
|
||||
} else {
|
||||
// Store original image and data
|
||||
[self storeImage:originalImage imageData:originalData forKey:key imageCache:imageCache cacheType:originalStoreCacheType finished:finished waitStoreCache:waitStoreCache completion:^{
|
||||
[self storeImage:originalImage imageData:originalData forKey:key options:options context:context imageCache:imageCache cacheType:originalStoreCacheType finished:finished completion:^{
|
||||
// Continue store cache process, transformed data is nil
|
||||
[self callStoreCacheProcessForOperation:operation url:url options:options context:context image:cacheImage data:cacheData cacheType:cacheType finished:finished completed:completedBlock];
|
||||
}];
|
||||
|
@ -590,7 +590,6 @@ static id<SDImageLoader> _defaultImageLoader;
|
|||
if (!imageCache) {
|
||||
imageCache = self.imageCache;
|
||||
}
|
||||
BOOL waitStoreCache = SD_OPTIONS_CONTAINS(options, SDWebImageWaitStoreCache);
|
||||
// the target image store cache type
|
||||
SDImageCacheType storeCacheType = SDImageCacheTypeAll;
|
||||
if (context[SDWebImageContextStoreCacheType]) {
|
||||
|
@ -604,14 +603,14 @@ static id<SDImageLoader> _defaultImageLoader;
|
|||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
|
||||
NSData *newData = [cacheSerializer cacheDataWithImage:image originalData:data imageURL:url];
|
||||
// Store image and data
|
||||
[self storeImage:image imageData:newData forKey:key imageCache:imageCache cacheType:storeCacheType finished:finished waitStoreCache:waitStoreCache completion:^{
|
||||
[self callCompletionBlockForOperation:operation completion:completedBlock image:image data:data error:nil cacheType:cacheType finished:finished url:url];
|
||||
[self storeImage:image imageData:newData forKey:key options:options context:context imageCache:imageCache cacheType:storeCacheType finished:finished completion:^{
|
||||
[self callCompletionBlockForOperation:operation completion:completedBlock image:image data:data error:nil cacheType:cacheType finished:finished queue:context[SDWebImageContextCallbackQueue] url:url];
|
||||
}];
|
||||
});
|
||||
} else {
|
||||
// Store image and data
|
||||
[self storeImage:image imageData:data forKey:key imageCache:imageCache cacheType:storeCacheType finished:finished waitStoreCache:waitStoreCache completion:^{
|
||||
[self callCompletionBlockForOperation:operation completion:completedBlock image:image data:data error:nil cacheType:cacheType finished:finished url:url];
|
||||
[self storeImage:image imageData:data forKey:key options:options context:context imageCache:imageCache cacheType:storeCacheType finished:finished completion:^{
|
||||
[self callCompletionBlockForOperation:operation completion:completedBlock image:image data:data error:nil cacheType:cacheType finished:finished queue:context[SDWebImageContextCallbackQueue] url:url];
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
@ -630,11 +629,13 @@ static id<SDImageLoader> _defaultImageLoader;
|
|||
- (void)storeImage:(nullable UIImage *)image
|
||||
imageData:(nullable NSData *)data
|
||||
forKey:(nullable NSString *)key
|
||||
options:(SDWebImageOptions)options
|
||||
context:(nullable SDWebImageContext *)context
|
||||
imageCache:(nonnull id<SDImageCache>)imageCache
|
||||
cacheType:(SDImageCacheType)cacheType
|
||||
finished:(BOOL)finished
|
||||
waitStoreCache:(BOOL)waitStoreCache
|
||||
completion:(nullable SDWebImageNoParamsBlock)completion {
|
||||
BOOL waitStoreCache = SD_OPTIONS_CONTAINS(options, SDWebImageWaitStoreCache);
|
||||
// Ignore progressive data cache
|
||||
if (!finished) {
|
||||
if (completion) {
|
||||
|
@ -643,13 +644,23 @@ static id<SDImageLoader> _defaultImageLoader;
|
|||
return;
|
||||
}
|
||||
// Check whether we should wait the store cache finished. If not, callback immediately
|
||||
[imageCache storeImage:image imageData:data forKey:key cacheType:cacheType completion:^{
|
||||
if (waitStoreCache) {
|
||||
if (completion) {
|
||||
completion();
|
||||
if ([imageCache respondsToSelector:@selector(storeImage:imageData:forKey:options:context:cacheType:completion:)]) {
|
||||
[imageCache storeImage:image imageData:data forKey:key options:options context:context cacheType:cacheType completion:^{
|
||||
if (waitStoreCache) {
|
||||
if (completion) {
|
||||
completion();
|
||||
}
|
||||
}
|
||||
}
|
||||
}];
|
||||
}];
|
||||
} else {
|
||||
[imageCache storeImage:image imageData:data forKey:key cacheType:cacheType completion:^{
|
||||
if (waitStoreCache) {
|
||||
if (completion) {
|
||||
completion();
|
||||
}
|
||||
}
|
||||
}];
|
||||
}
|
||||
if (!waitStoreCache) {
|
||||
if (completion) {
|
||||
completion();
|
||||
|
@ -660,8 +671,9 @@ static id<SDImageLoader> _defaultImageLoader;
|
|||
- (void)callCompletionBlockForOperation:(nullable SDWebImageCombinedOperation*)operation
|
||||
completion:(nullable SDInternalCompletionBlock)completionBlock
|
||||
error:(nullable NSError *)error
|
||||
queue:(nullable SDCallbackQueue *)queue
|
||||
url:(nullable NSURL *)url {
|
||||
[self callCompletionBlockForOperation:operation completion:completionBlock image:nil data:nil error:error cacheType:SDImageCacheTypeNone finished:YES url:url];
|
||||
[self callCompletionBlockForOperation:operation completion:completionBlock image:nil data:nil error:error cacheType:SDImageCacheTypeNone finished:YES queue:queue url:url];
|
||||
}
|
||||
|
||||
- (void)callCompletionBlockForOperation:(nullable SDWebImageCombinedOperation*)operation
|
||||
|
@ -671,11 +683,12 @@ static id<SDImageLoader> _defaultImageLoader;
|
|||
error:(nullable NSError *)error
|
||||
cacheType:(SDImageCacheType)cacheType
|
||||
finished:(BOOL)finished
|
||||
queue:(nullable SDCallbackQueue *)queue
|
||||
url:(nullable NSURL *)url {
|
||||
if (completionBlock) {
|
||||
dispatch_main_async_safe(^{
|
||||
[(queue ?: SDCallbackQueue.mainQueue) async:^{
|
||||
completionBlock(image, data, error, cacheType, finished, url);
|
||||
});
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -76,20 +76,23 @@ typedef void(^SDWebImagePrefetcherCompletionBlock)(NSUInteger noOfFinishedUrls,
|
|||
|
||||
/**
|
||||
* The options for prefetcher. Defaults to SDWebImageLowPriority.
|
||||
* @deprecated Prefetcher is designed to be used shared and should not effect others. So in 5.15.0 we added API `prefetchURLs:options:context:`. If you want global control, try to use `SDWebImageOptionsProcessor` in manager level.
|
||||
*/
|
||||
@property (nonatomic, assign) SDWebImageOptions options;
|
||||
@property (nonatomic, assign) SDWebImageOptions options API_DEPRECATED("Use individual prefetch options param instead", macos(10.10, API_TO_BE_DEPRECATED), ios(8.0, API_TO_BE_DEPRECATED), tvos(9.0, API_TO_BE_DEPRECATED), watchos(2.0, API_TO_BE_DEPRECATED));
|
||||
|
||||
/**
|
||||
* The context for prefetcher. Defaults to nil.
|
||||
* @deprecated Prefetcher is designed to be used shared and should not effect others. So in 5.15.0 we added API `prefetchURLs:options:context:`. If you want global control, try to use `SDWebImageOptionsProcessor` in `SDWebImageManager.optionsProcessor`.
|
||||
*/
|
||||
@property (nonatomic, copy, nullable) SDWebImageContext *context;
|
||||
@property (nonatomic, copy, nullable) SDWebImageContext *context API_DEPRECATED("Use individual prefetch context param instead", macos(10.10, API_TO_BE_DEPRECATED), ios(8.0, API_TO_BE_DEPRECATED), tvos(9.0, API_TO_BE_DEPRECATED), watchos(2.0, API_TO_BE_DEPRECATED));
|
||||
|
||||
/**
|
||||
* Queue options for prefetcher when call the progressBlock, completionBlock and delegate methods. Defaults to Main Queue.
|
||||
* @note The call is asynchronously to avoid blocking target queue.
|
||||
* @deprecated 5.15.0 introduce SDCallbackQueue, use that is preferred and has higher priority. The set/get to this property will translate to that instead.
|
||||
* @note The call is asynchronously to avoid blocking target queue. (see SDCallbackPolicyDispatch)
|
||||
* @note The delegate queue should be set before any prefetching start and may not be changed during prefetching to avoid thread-safe problem.
|
||||
*/
|
||||
@property (strong, nonatomic, nonnull) dispatch_queue_t delegateQueue;
|
||||
@property (strong, nonatomic, nonnull) dispatch_queue_t delegateQueue API_DEPRECATED("Use SDWebImageContextCallbackQueue context param instead, see SDCallbackQueue", macos(10.10, 10.10), ios(8.0, 8.0), tvos(9.0, 9.0), watchos(2.0, 2.0));
|
||||
|
||||
/**
|
||||
* The delegate for the prefetcher. Defaults to nil.
|
||||
|
@ -134,6 +137,28 @@ typedef void(^SDWebImagePrefetcherCompletionBlock)(NSUInteger noOfFinishedUrls,
|
|||
progress:(nullable SDWebImagePrefetcherProgressBlock)progressBlock
|
||||
completed:(nullable SDWebImagePrefetcherCompletionBlock)completionBlock;
|
||||
|
||||
/**
|
||||
* Assign list of URLs to let SDWebImagePrefetcher to queue the prefetching. It based on the image manager so the image may from the cache and network according to the `options` property.
|
||||
* Prefetching is separate to each other, which means the progressBlock and completionBlock you provide is bind to the prefetching for the list of urls.
|
||||
* Attention that call this will not cancel previous fetched urls. You should keep the token return by this to cancel or cancel all the prefetch.
|
||||
*
|
||||
* @param urls list of URLs to prefetch
|
||||
* @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values.
|
||||
* @param context A context contains different options to perform specify changes or processes, see `SDWebImageContextOption`. This hold the extra objects which `options` enum can not hold.
|
||||
* @param progressBlock block to be called when progress updates;
|
||||
* first parameter is the number of completed (successful or not) requests,
|
||||
* second parameter is the total number of images originally requested to be prefetched
|
||||
* @param completionBlock block to be called when the current prefetching is completed
|
||||
* first param is the number of completed (successful or not) requests,
|
||||
* second parameter is the number of skipped requests
|
||||
* @return the token to cancel the current prefetching.
|
||||
*/
|
||||
- (nullable SDWebImagePrefetchToken *)prefetchURLs:(nullable NSArray<NSURL *> *)urls
|
||||
options:(SDWebImageOptions)options
|
||||
context:(nullable SDWebImageContext *)context
|
||||
progress:(nullable SDWebImagePrefetcherProgressBlock)progressBlock
|
||||
completed:(nullable SDWebImagePrefetcherCompletionBlock)completionBlock;
|
||||
|
||||
/**
|
||||
* Remove and cancel all the prefeching for the prefetcher.
|
||||
*/
|
||||
|
|
|
@ -8,9 +8,16 @@
|
|||
|
||||
#import "SDWebImagePrefetcher.h"
|
||||
#import "SDAsyncBlockOperation.h"
|
||||
#import "SDCallbackQueue.h"
|
||||
#import "SDInternalMacros.h"
|
||||
#import <stdatomic.h>
|
||||
|
||||
@interface SDCallbackQueue ()
|
||||
|
||||
@property (nonatomic, strong, nonnull) dispatch_queue_t queue;
|
||||
|
||||
@end
|
||||
|
||||
@interface SDWebImagePrefetchToken () {
|
||||
@public
|
||||
// Though current implementation, `SDWebImageManager` completion block is always on main queue. But however, there is no guarantee in docs. And we may introduce config to specify custom queue in the future.
|
||||
|
@ -30,6 +37,8 @@
|
|||
@property (nonatomic, strong) NSPointerArray *loadOperations;
|
||||
@property (nonatomic, strong) NSPointerArray *prefetchOperations;
|
||||
@property (nonatomic, weak) SDWebImagePrefetcher *prefetcher;
|
||||
@property (nonatomic, assign) SDWebImageOptions options;
|
||||
@property (nonatomic, copy, nullable) SDWebImageContext *context;
|
||||
@property (nonatomic, copy, nullable) SDWebImagePrefetcherCompletionBlock completionBlock;
|
||||
@property (nonatomic, copy, nullable) SDWebImagePrefetcherProgressBlock progressBlock;
|
||||
|
||||
|
@ -40,6 +49,7 @@
|
|||
@property (strong, nonatomic, nonnull) SDWebImageManager *manager;
|
||||
@property (strong, atomic, nonnull) NSMutableSet<SDWebImagePrefetchToken *> *runningTokens;
|
||||
@property (strong, nonatomic, nonnull) NSOperationQueue *prefetchQueue;
|
||||
@property (strong, nonatomic, nullable) SDCallbackQueue *callbackQueue;
|
||||
|
||||
@end
|
||||
|
||||
|
@ -63,7 +73,6 @@
|
|||
_manager = manager;
|
||||
_runningTokens = [NSMutableSet set];
|
||||
_options = SDWebImageLowPriority;
|
||||
_delegateQueue = dispatch_get_main_queue();
|
||||
_prefetchQueue = [NSOperationQueue new];
|
||||
self.maxConcurrentPrefetchCount = 3;
|
||||
}
|
||||
|
@ -78,6 +87,17 @@
|
|||
return self.prefetchQueue.maxConcurrentOperationCount;
|
||||
}
|
||||
|
||||
- (void)setDelegateQueue:(dispatch_queue_t)delegateQueue {
|
||||
// Deprecate and translate to SDCallbackQueue
|
||||
_callbackQueue = [[SDCallbackQueue alloc] initWithDispatchQueue:delegateQueue];
|
||||
_callbackQueue.policy = SDCallbackPolicyDispatch;
|
||||
}
|
||||
|
||||
- (dispatch_queue_t)delegateQueue {
|
||||
// Deprecate and translate to SDCallbackQueue
|
||||
return (_callbackQueue ?: SDCallbackQueue.mainQueue).queue;
|
||||
}
|
||||
|
||||
#pragma mark - Prefetch
|
||||
- (nullable SDWebImagePrefetchToken *)prefetchURLs:(nullable NSArray<NSURL *> *)urls {
|
||||
return [self prefetchURLs:urls progress:nil completed:nil];
|
||||
|
@ -86,6 +106,14 @@
|
|||
- (nullable SDWebImagePrefetchToken *)prefetchURLs:(nullable NSArray<NSURL *> *)urls
|
||||
progress:(nullable SDWebImagePrefetcherProgressBlock)progressBlock
|
||||
completed:(nullable SDWebImagePrefetcherCompletionBlock)completionBlock {
|
||||
return [self prefetchURLs:urls options:self.options context:self.context progress:progressBlock completed:completionBlock];
|
||||
}
|
||||
|
||||
- (nullable SDWebImagePrefetchToken *)prefetchURLs:(nullable NSArray<NSURL *> *)urls
|
||||
options:(SDWebImageOptions)options
|
||||
context:(nullable SDWebImageContext *)context
|
||||
progress:(nullable SDWebImagePrefetcherProgressBlock)progressBlock
|
||||
completed:(nullable SDWebImagePrefetcherCompletionBlock)completionBlock {
|
||||
if (!urls || urls.count == 0) {
|
||||
if (completionBlock) {
|
||||
completionBlock(0, 0);
|
||||
|
@ -95,6 +123,8 @@
|
|||
SDWebImagePrefetchToken *token = [SDWebImagePrefetchToken new];
|
||||
token.prefetcher = self;
|
||||
token.urls = urls;
|
||||
token.options = options;
|
||||
token.context = context;
|
||||
token->_skippedCount = 0;
|
||||
token->_finishedCount = 0;
|
||||
token->_totalCount = token.urls.count;
|
||||
|
@ -117,7 +147,7 @@
|
|||
if (!self || asyncOperation.isCancelled) {
|
||||
return;
|
||||
}
|
||||
id<SDWebImageOperation> operation = [self.manager loadImageWithURL:url options:self.options context:self.context progress:nil completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, SDImageCacheType cacheType, BOOL finished, NSURL * _Nullable imageURL) {
|
||||
id<SDWebImageOperation> operation = [self.manager loadImageWithURL:url options:token.options context:token.context progress:nil completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, SDImageCacheType cacheType, BOOL finished, NSURL * _Nullable imageURL) {
|
||||
@strongify(self);
|
||||
if (!self) {
|
||||
return;
|
||||
|
@ -173,14 +203,18 @@
|
|||
NSUInteger tokenTotalCount = [self tokenTotalCount];
|
||||
NSUInteger finishedCount = atomic_load_explicit(&(token->_finishedCount), memory_order_relaxed);
|
||||
NSUInteger totalCount = token->_totalCount;
|
||||
dispatch_async(self.delegateQueue, ^{
|
||||
SDCallbackQueue *queue = token.context[SDWebImageContextCallbackQueue];
|
||||
if (!queue) {
|
||||
queue = self.callbackQueue;
|
||||
}
|
||||
[(queue ?: SDCallbackQueue.mainQueue) async:^{
|
||||
if (shouldCallDelegate) {
|
||||
[self.delegate imagePrefetcher:self didPrefetchURL:url finishedCount:tokenFinishedCount totalCount:tokenTotalCount];
|
||||
}
|
||||
if (token.progressBlock) {
|
||||
token.progressBlock(finishedCount, totalCount);
|
||||
}
|
||||
});
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)callCompletionBlockForToken:(SDWebImagePrefetchToken *)token {
|
||||
|
@ -192,14 +226,18 @@
|
|||
NSUInteger tokenSkippedCount = [self tokenSkippedCount];
|
||||
NSUInteger finishedCount = atomic_load_explicit(&(token->_finishedCount), memory_order_relaxed);
|
||||
NSUInteger skippedCount = atomic_load_explicit(&(token->_skippedCount), memory_order_relaxed);
|
||||
dispatch_async(self.delegateQueue, ^{
|
||||
SDCallbackQueue *queue = token.context[SDWebImageContextCallbackQueue];
|
||||
if (!queue) {
|
||||
queue = self.callbackQueue;
|
||||
}
|
||||
[(queue ?: SDCallbackQueue.mainQueue) async:^{
|
||||
if (shoulCallDelegate) {
|
||||
[self.delegate imagePrefetcher:self didFinishWithTotalCount:tokenTotalCount skippedCount:tokenSkippedCount];
|
||||
}
|
||||
if (token.completionBlock) {
|
||||
token.completionBlock(finishedCount, skippedCount);
|
||||
}
|
||||
});
|
||||
}];
|
||||
}
|
||||
|
||||
#pragma mark - Helper
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
../../Core/SDCallbackQueue.h
|
|
@ -660,12 +660,18 @@ static NSString *kTestImageKeyPNG = @"TestImageKey.png";
|
|||
|
||||
SDImageCache *cache = [[SDImageCache alloc] initWithNamespace:@"Concurrent" diskCacheDirectory:@"/" config:config];
|
||||
NSData *pngData = [NSData dataWithContentsOfFile:[self testPNGPath]];
|
||||
[cache queryCacheOperationForKey:@"Key1" done:^(UIImage * _Nullable image, NSData * _Nullable data, SDImageCacheType cacheType) {
|
||||
expect(data).beNil();
|
||||
[expectation fulfill];
|
||||
}];
|
||||
[cache storeImageData:pngData forKey:@"Key1" completion:^{
|
||||
[expectation fulfill];
|
||||
// Added test case for custom queue
|
||||
[SDCallbackQueue.globalQueue async:^{
|
||||
SDWebImageContext *context = @{SDWebImageContextCallbackQueue : SDCallbackQueue.currentQueue};
|
||||
expect(NSThread.isMainThread).beFalsy();
|
||||
[cache queryCacheOperationForKey:@"Key1" options:0 context:context done:^(UIImage * _Nullable image, NSData * _Nullable data, SDImageCacheType cacheType) {
|
||||
expect(data).beNil();
|
||||
expect(NSThread.isMainThread).beFalsy();
|
||||
[expectation fulfill];
|
||||
}];
|
||||
[cache storeImageData:pngData forKey:@"Key1" completion:^{
|
||||
[expectation fulfill];
|
||||
}];
|
||||
}];
|
||||
|
||||
[self waitForExpectationsWithCommonTimeout];
|
||||
|
|
|
@ -154,6 +154,56 @@
|
|||
expect(scaledImage.scale).equal(2);
|
||||
}
|
||||
|
||||
- (void)testSDCallbackQueue {
|
||||
XCTestExpectation *expectation1 = [self expectationWithDescription:@"SDCallbackQueue SafeExecute works"];
|
||||
XCTestExpectation *expectation2 = [self expectationWithDescription:@"SDCallbackQueue Dispatch works"];
|
||||
XCTestExpectation *expectation3 = [self expectationWithDescription:@"SDCallbackQueue Invoke works"];
|
||||
dispatch_queue_t queue = dispatch_queue_create("testSDCallbackQueue", NULL);
|
||||
SDCallbackQueue *callbackQueue = [[SDCallbackQueue alloc] initWithDispatchQueue:queue];
|
||||
__block BOOL called = NO;
|
||||
[callbackQueue sync:^{
|
||||
called = YES;
|
||||
}];
|
||||
expect(called).beTruthy();
|
||||
|
||||
__block BOOL called1 = NO;
|
||||
callbackQueue.policy = SDCallbackPolicySafeExecute;
|
||||
dispatch_async(queue, ^{
|
||||
// Should execute in sync
|
||||
[callbackQueue async:^{
|
||||
called1 = YES;
|
||||
[expectation1 fulfill];
|
||||
}];
|
||||
expect(called1).beTruthy();
|
||||
});
|
||||
|
||||
SDCallbackQueue *callbackQueue2 = [[SDCallbackQueue alloc] initWithDispatchQueue:queue];
|
||||
__block BOOL called2 = NO;
|
||||
callbackQueue2.policy = SDCallbackPolicyDispatch;
|
||||
dispatch_async(queue, ^{
|
||||
// Should execute in async
|
||||
[callbackQueue2 async:^{
|
||||
called2 = YES;
|
||||
[expectation2 fulfill];
|
||||
}];
|
||||
expect(called2).beFalsy();
|
||||
});
|
||||
|
||||
SDCallbackQueue *callbackQueue3 = [[SDCallbackQueue alloc] initWithDispatchQueue:queue];
|
||||
__block BOOL called3 = NO;
|
||||
callbackQueue3.policy = SDCallbackPolicyInvoke;
|
||||
dispatch_async(queue, ^{
|
||||
// Should execute in sync
|
||||
[callbackQueue3 async:^{
|
||||
called3 = YES;
|
||||
[expectation3 fulfill];
|
||||
}];
|
||||
expect(called3).beTruthy();
|
||||
});
|
||||
|
||||
[self waitForExpectationsWithCommonTimeout];
|
||||
}
|
||||
|
||||
- (void)testInternalMacro {
|
||||
@weakify(self);
|
||||
@onExit {
|
||||
|
|
|
@ -265,6 +265,10 @@ static NSString * const SDWebImageTestDiskCacheExtendedAttributeName = @"com.hac
|
|||
}
|
||||
|
||||
- (void)storeImage:(nullable UIImage *)image imageData:(nullable NSData *)imageData forKey:(nullable NSString *)key cacheType:(SDImageCacheType)cacheType completion:(nullable SDWebImageNoParamsBlock)completionBlock {
|
||||
[self storeImage:image imageData:imageData forKey:key options:0 context:nil cacheType:cacheType completion:completionBlock];
|
||||
}
|
||||
|
||||
- (void)storeImage:(nullable UIImage *)image imageData:(nullable NSData *)imageData forKey:(nullable NSString *)key options:(SDWebImageOptions)options context:(nullable SDWebImageContext *)context cacheType:(SDImageCacheType)cacheType completion:(nullable SDWebImageNoParamsBlock)completionBlock {
|
||||
switch (cacheType) {
|
||||
case SDImageCacheTypeNone:
|
||||
break;
|
||||
|
|
|
@ -18,6 +18,7 @@ FOUNDATION_EXPORT const unsigned char SDWebImageVersionString[];
|
|||
// In this header, you should import all the public headers of your framework using statements like #import <SDWebImage/PublicHeader.h>
|
||||
|
||||
#import <SDWebImage/SDWebImageManager.h>
|
||||
#import <SDWebImage/SDCallbackQueue.h>
|
||||
#import <SDWebImage/SDWebImageCacheKeyFilter.h>
|
||||
#import <SDWebImage/SDWebImageCacheSerializer.h>
|
||||
#import <SDWebImage/SDImageCacheConfig.h>
|
||||
|
|
Loading…
Reference in New Issue