diff --git a/SDWebImage/Core/SDCallbackQueue.h b/SDWebImage/Core/SDCallbackQueue.h index eeab543a..ea84ff7f 100644 --- a/SDWebImage/Core/SDCallbackQueue.h +++ b/SDWebImage/Core/SDCallbackQueue.h @@ -17,8 +17,10 @@ typedef NS_ENUM(NSUInteger, SDCallbackPolicy) { SDCallbackPolicyDispatch = 1, /// Ignore any async/sync and just directly invoke `block` in current queue (without `dispatch_async`/`dispatch_sync`) SDCallbackPolicyInvoke = 2, - /// Ensure callback in main queue and main thread, will do `dispatch_async` if the current queue is not main queue; else do invoke `block`. Never use `dispatch_sync`, suitable for UI-related work - SDCallbackPolicyMainAsyncSafe = 3 + /// Ensure callback in main queue (no gurantee on main thread). Do `dispatch_async` if the current queue is not main queue; else do invoke `block`. Never use `dispatch_sync`, suitable for general UI-related code + SDCallbackPolicySafeAsyncMainQueue = 3, + /// Ensure callback in main thread. Do `dispatch_async` if the `NSThread.isMainTrhead == true` ; else do invoke `block`. Never use `dispatch_sync`, suitable for special UI-related code + SDCallbackPolicySafeAsyncMainThread = 4, }; /// SDCallbackQueue is a wrapper used to control how the completionBlock should perform on queues, used by our `Cache`/`Manager`/`Loader`. @@ -34,7 +36,9 @@ typedef NS_ENUM(NSUInteger, SDCallbackPolicy) { /// 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 `SDCallbackPolicyMainAsyncSafe`, which behaves like the old macro `dispatch_main_async_safe` +/// The current queue's callback policy. +/// defaults to `SDCallbackPolicySafeAsyncMainQueue` from v5.20.1, which behaves like the old macro `dispatch_main_async_safe` +/// @note old 5.x version use `SDCallbackPolicySafeExecute`, which may behave incorrectly when you sync the block into global queue. @property (assign, readwrite) SDCallbackPolicy policy; - (nonnull instancetype)init NS_UNAVAILABLE; diff --git a/SDWebImage/Core/SDCallbackQueue.m b/SDWebImage/Core/SDCallbackQueue.m index ca6b1451..3b578d8d 100644 --- a/SDWebImage/Core/SDCallbackQueue.m +++ b/SDWebImage/Core/SDCallbackQueue.m @@ -21,7 +21,7 @@ static void SDReleaseBlock(void *context) { CFRelease(context); } -static inline void SDSafeMainAsync(dispatch_block_t _Nonnull block) { +static inline void SDSafeMainQueueAsync(dispatch_block_t _Nonnull block) { if (NSThread.isMainThread) { // Match exists `dispatch_main_async_safe` behavior const char *currentLabel = dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL); @@ -34,6 +34,14 @@ static inline void SDSafeMainAsync(dispatch_block_t _Nonnull block) { dispatch_async(dispatch_get_main_queue(), block); } +static inline void SDSafeMainThreadAsync(dispatch_block_t _Nonnull block) { + if (NSThread.isMainThread) { + block(); + } else { + dispatch_async(dispatch_get_main_queue(), block); + } +} + static void SDSafeExecute(SDCallbackQueue *callbackQueue, dispatch_block_t _Nonnull block, BOOL async) { // Extend gcd queue's life cycle dispatch_queue_t queue = callbackQueue.queue; @@ -71,7 +79,7 @@ static void SDSafeExecute(SDCallbackQueue *callbackQueue, dispatch_block_t _Nonn CFUUIDRef UUID = CFUUIDCreate(kCFAllocatorDefault); dispatch_queue_set_specific(queue, SDCallbackQueueKey, (void *)UUID, SDReleaseBlock); _queue = queue; - _policy = SDCallbackPolicyMainAsyncSafe; + _policy = SDCallbackPolicySafeAsyncMainQueue; // global queue can execute on main thread, like call `dispatch_sync(globalQueue)` in main thread _thread = NSThread.currentThread; } @@ -111,8 +119,11 @@ static void SDSafeExecute(SDCallbackQueue *callbackQueue, dispatch_block_t _Nonn case SDCallbackPolicyInvoke: block(); break; - case SDCallbackPolicyMainAsyncSafe: - SDSafeMainAsync(block); + case SDCallbackPolicySafeAsyncMainQueue: + SDSafeMainQueueAsync(block); + break; + case SDCallbackPolicySafeAsyncMainThread: + SDSafeMainThreadAsync(block); break; default: SDSafeExecute(self, block, NO); @@ -131,8 +142,11 @@ static void SDSafeExecute(SDCallbackQueue *callbackQueue, dispatch_block_t _Nonn case SDCallbackPolicyInvoke: block(); break; - case SDCallbackPolicyMainAsyncSafe: - SDSafeMainAsync(block); + case SDCallbackPolicySafeAsyncMainQueue: + SDSafeMainQueueAsync(block); + break; + case SDCallbackPolicySafeAsyncMainThread: + SDSafeMainThreadAsync(block); break; default: SDSafeExecute(self, block, YES);