Change the asyncSafe to the queue level configuration, introduce SDCallbackPolicy
This commit is contained in:
parent
67520b9f55
commit
5a4b4cf16d
|
@ -9,6 +9,16 @@
|
|||
|
||||
#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
|
||||
|
@ -22,26 +32,21 @@
|
|||
/// 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;
|
||||
|
||||
/// 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;
|
||||
|
||||
#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;
|
||||
|
||||
/// 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:(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;
|
||||
|
||||
/// 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:(nonnull NS_NOESCAPE dispatch_block_t)block;
|
||||
|
||||
@end
|
||||
|
|
|
@ -20,6 +20,27 @@ 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, 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(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 {
|
||||
|
@ -56,44 +77,30 @@ static void SDReleaseBlock(void *context) {
|
|||
}
|
||||
|
||||
- (void)sync:(nonnull NS_NOESCAPE dispatch_block_t)block {
|
||||
dispatch_sync(self.queue, block);
|
||||
}
|
||||
|
||||
- (void)syncSafe:(nonnull NS_NOESCAPE dispatch_block_t)block {
|
||||
// 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);
|
||||
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 {
|
||||
dispatch_async(self.queue, block);
|
||||
}
|
||||
|
||||
- (void)asyncSafe:(nonnull NS_NOESCAPE dispatch_block_t)block {
|
||||
// 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);
|
||||
switch (self.policy) {
|
||||
case SDCallbackPolicySafeExecute:
|
||||
SDSafeExecute(self.queue, block, YES);
|
||||
break;
|
||||
case SDCallbackPolicyDispatch:
|
||||
dispatch_async(self.queue, block);
|
||||
break;
|
||||
case SDCallbackPolicyInvoke:
|
||||
block();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -45,7 +45,7 @@
|
|||
SDImageCacheQueryCompletionBlock doneBlock = self.doneBlock;
|
||||
self.doneBlock = nil;
|
||||
if (doneBlock) {
|
||||
[(self.queue ?: SDCallbackQueue.mainQueue) asyncSafe:^{
|
||||
[(self.queue ?: SDCallbackQueue.mainQueue) async:^{
|
||||
doneBlock(nil, nil, SDImageCacheTypeNone);
|
||||
}];
|
||||
}
|
||||
|
|
|
@ -696,7 +696,7 @@ didReceiveResponse:(NSURLResponse *)response
|
|||
SDWebImageDownloaderCompletedBlock completedBlock = token.completedBlock;
|
||||
if (completedBlock) {
|
||||
SDCallbackQueue *queue = self.context[SDWebImageContextCallbackQueue];
|
||||
[(queue ?: SDCallbackQueue.mainQueue) asyncSafe:^{
|
||||
[(queue ?: SDCallbackQueue.mainQueue) async:^{
|
||||
completedBlock(image, imageData, error, finished);
|
||||
}];
|
||||
}
|
||||
|
@ -711,7 +711,7 @@ didReceiveResponse:(NSURLResponse *)response
|
|||
SDWebImageDownloaderCompletedBlock completedBlock = token.completedBlock;
|
||||
if (completedBlock) {
|
||||
SDCallbackQueue *queue = self.context[SDWebImageContextCallbackQueue];
|
||||
[(queue ?: SDCallbackQueue.mainQueue) asyncSafe:^{
|
||||
[(queue ?: SDCallbackQueue.mainQueue) async:^{
|
||||
completedBlock(image, imageData, error, finished);
|
||||
}];
|
||||
}
|
||||
|
|
|
@ -676,7 +676,7 @@ static id<SDImageLoader> _defaultImageLoader;
|
|||
queue:(nullable SDCallbackQueue *)queue
|
||||
url:(nullable NSURL *)url {
|
||||
if (completionBlock) {
|
||||
[(queue ?: SDCallbackQueue.mainQueue) asyncSafe:^{
|
||||
[(queue ?: SDCallbackQueue.mainQueue) async:^{
|
||||
completionBlock(image, data, error, cacheType, finished, url);
|
||||
}];
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue