Implements the Callback queue dispatch
Fix some missing components
This commit is contained in:
parent
43ec4726e1
commit
b5d712a378
|
@ -9,24 +9,39 @@
|
|||
|
||||
#import "SDWebImageDefine.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/// 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;
|
||||
|
||||
@property (nonnull, class, readonly) SDCallbackQueue *callerQueue;
|
||||
/// 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;
|
||||
|
||||
+ (SDCallbackQueue *)dispatchQueue:(dispatch_queue_t)queue;
|
||||
/// 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;
|
||||
|
||||
- (void)sync:(SDWebImageNoParamsBlock)block;
|
||||
/// Submits a block for execution and returns after that block finishes executing.
|
||||
/// - Parameter block: The block that contains the work to perform.
|
||||
- (void)sync:(nullable SDWebImageNoParamsBlock)block;
|
||||
|
||||
- (void)async:(SDWebImageNoParamsBlock)block;
|
||||
/// Submits a block for execution and returns after that block finishes executing. When the current enqueued queue matching the callback queue, call the block immediately.
|
||||
/// @warning This will not works when using `dispatch_set_target_queue` or recursive queue context.
|
||||
/// - Parameter block: The block that contains the work to perform.
|
||||
- (void)syncSafe:(nullable SDWebImageNoParamsBlock)block;
|
||||
|
||||
- (void)asyncSafe:(SDWebImageNoParamsBlock)block;
|
||||
/// Schedules a block asynchronously for execution.
|
||||
/// - Parameter block: The block that contains the work to perform.
|
||||
- (void)async:(nullable SDWebImageNoParamsBlock)block;
|
||||
|
||||
/// Schedules a block asynchronously for execution. When the current enqueued queue matching the callback queue, call the block immediately.
|
||||
/// @warning This will not works when using `dispatch_set_target_queue` or recursive queue context.
|
||||
/// - Parameter block: The block that contains the work to perform.
|
||||
- (void)asyncSafe:(nullable SDWebImageNoParamsBlock)block;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
|
|
@ -9,10 +9,96 @@
|
|||
|
||||
#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);
|
||||
}
|
||||
|
||||
@implementation SDCallbackQueue
|
||||
|
||||
- (void)sync:(dispatch_block_t)block {
|
||||
|
||||
- (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:(SDWebImageNoParamsBlock)block {
|
||||
if (!block) return;
|
||||
dispatch_sync(self.queue, block);
|
||||
}
|
||||
|
||||
- (void)syncSafe:(SDWebImageNoParamsBlock)block {
|
||||
if (!block) return;
|
||||
// Special handle for main queue, faster
|
||||
const char *currentLabel = dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL);
|
||||
if (currentLabel && currentLabel == dispatch_queue_get_label(dispatch_get_main_queue())) {
|
||||
block();
|
||||
return;
|
||||
}
|
||||
// Check specific to detect queue equal
|
||||
void *specific = dispatch_queue_get_specific(self.queue, SDCallbackQueueKey);
|
||||
void *currentSpecific = dispatch_get_specific(SDCallbackQueueKey);
|
||||
if (specific && currentSpecific && CFGetTypeID(specific) == CFUUIDGetTypeID() && CFGetTypeID(currentSpecific) == CFUUIDGetTypeID() && CFEqual(specific, currentSpecific)) {
|
||||
block();
|
||||
} else {
|
||||
dispatch_sync(self.queue, block);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)async:(SDWebImageNoParamsBlock)block {
|
||||
if (!block) return;
|
||||
dispatch_async(self.queue, block);
|
||||
}
|
||||
|
||||
- (void)asyncSafe:(SDWebImageNoParamsBlock)block {
|
||||
if (!block) return;
|
||||
// Special handle for main queue, faster
|
||||
const char *currentLabel = dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL);
|
||||
if (currentLabel && currentLabel == dispatch_queue_get_label(dispatch_get_main_queue())) {
|
||||
block();
|
||||
return;
|
||||
}
|
||||
// Check specific to detect queue equal
|
||||
void *specific = dispatch_queue_get_specific(self.queue, SDCallbackQueueKey);
|
||||
void *currentSpecific = dispatch_get_specific(SDCallbackQueueKey);
|
||||
if (specific && currentSpecific && CFGetTypeID(specific) == CFUUIDGetTypeID() && CFGetTypeID(currentSpecific) == CFUUIDGetTypeID() && CFEqual(specific, currentSpecific)) {
|
||||
block();
|
||||
} else {
|
||||
dispatch_async(self.queue, block);
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
@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 *queue;
|
||||
|
||||
@end
|
||||
|
||||
|
@ -44,9 +45,9 @@
|
|||
SDImageCacheQueryCompletionBlock doneBlock = self.doneBlock;
|
||||
self.doneBlock = nil;
|
||||
if (doneBlock) {
|
||||
dispatch_main_async_safe(^{
|
||||
[(self.queue ?: SDCallbackQueue.mainQueue) asyncSafe:^{
|
||||
doneBlock(nil, nil, SDImageCacheTypeNone);
|
||||
});
|
||||
}];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -609,14 +610,15 @@ static NSString * _defaultDiskCacheDirectory;
|
|||
}
|
||||
|
||||
// Second check the disk cache...
|
||||
SDCallbackQueue *queue = context[SDWebImageContextCallbackQueue];
|
||||
SDImageCacheToken *operation = [[SDImageCacheToken alloc] initWithDoneBlock:doneBlock];
|
||||
operation.key = key;
|
||||
operation.queue = queue;
|
||||
// Check whether we need to synchronously query disk
|
||||
// 1. in-memory cache hit & memoryDataSync
|
||||
// 2. in-memory cache miss & diskDataSync
|
||||
BOOL shouldQueryDiskSync = ((image && options & SDImageCacheQueryMemoryDataSync) ||
|
||||
(!image && options & SDImageCacheQueryDiskDataSync));
|
||||
SDCallbackQueue *queue = context[SDWebImageContextCallbackQueue];
|
||||
NSData* (^queryDiskDataBlock)(void) = ^NSData* {
|
||||
@synchronized (operation) {
|
||||
if (operation.isCancelled) {
|
||||
|
|
|
@ -222,7 +222,11 @@ 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;
|
||||
|
||||
|
|
|
@ -158,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;
|
||||
}
|
||||
|
|
|
@ -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