Skip to content

Commit 05f7fb9

Browse files
authored
Merge pull request SDWebImage#3465 from dreampiggy/feat/callback_queue
Added context option `callbackQueue` and `SDCallbackQueue` wrapper for advanced user to control which queue to callback
2 parents 4178d12 + 720a097 commit 05f7fb9

19 files changed

+464
-110
lines changed

SDWebImage.xcodeproj/project.pbxproj

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
320CAE172086F50500CFFC80 /* SDWebImageError.h in Headers */ = {isa = PBXBuildFile; fileRef = 320CAE132086F50500CFFC80 /* SDWebImageError.h */; settings = {ATTRIBUTES = (Public, ); }; };
2626
320CAE1B2086F50500CFFC80 /* SDWebImageError.m in Sources */ = {isa = PBXBuildFile; fileRef = 320CAE142086F50500CFFC80 /* SDWebImageError.m */; };
2727
320CAE1D2086F50500CFFC80 /* SDWebImageError.m in Sources */ = {isa = PBXBuildFile; fileRef = 320CAE142086F50500CFFC80 /* SDWebImageError.m */; };
28+
321117A9296573680001FC2C /* SDCallbackQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 321117A7296573680001FC2C /* SDCallbackQueue.h */; settings = {ATTRIBUTES = (Public, ); }; };
29+
321117AA296573680001FC2C /* SDCallbackQueue.m in Sources */ = {isa = PBXBuildFile; fileRef = 321117A8296573680001FC2C /* SDCallbackQueue.m */; };
2830
321B37832083290E00C0EA77 /* SDImageLoader.h in Headers */ = {isa = PBXBuildFile; fileRef = 321B377D2083290D00C0EA77 /* SDImageLoader.h */; settings = {ATTRIBUTES = (Public, ); }; };
2931
321B37872083290E00C0EA77 /* SDImageLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = 321B377E2083290D00C0EA77 /* SDImageLoader.m */; };
3032
321B37892083290E00C0EA77 /* SDImageLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = 321B377E2083290D00C0EA77 /* SDImageLoader.m */; };
@@ -67,6 +69,7 @@
6769
324DF4B6200A14DC008A84CC /* SDWebImageDefine.h in Headers */ = {isa = PBXBuildFile; fileRef = 324DF4B2200A14DC008A84CC /* SDWebImageDefine.h */; settings = {ATTRIBUTES = (Public, ); }; };
6870
324DF4BA200A14DC008A84CC /* SDWebImageDefine.m in Sources */ = {isa = PBXBuildFile; fileRef = 324DF4B3200A14DC008A84CC /* SDWebImageDefine.m */; };
6971
324DF4BC200A14DC008A84CC /* SDWebImageDefine.m in Sources */ = {isa = PBXBuildFile; fileRef = 324DF4B3200A14DC008A84CC /* SDWebImageDefine.m */; };
72+
325074F2296C546D00B730CF /* SDCallbackQueue.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 321117A7296573680001FC2C /* SDCallbackQueue.h */; };
7073
3250C9EE2355D9DA0093A896 /* SDWebImageDownloaderDecryptor.h in Headers */ = {isa = PBXBuildFile; fileRef = 3250C9EC2355D9DA0093A896 /* SDWebImageDownloaderDecryptor.h */; settings = {ATTRIBUTES = (Public, ); }; };
7174
3250C9EF2355D9DA0093A896 /* SDWebImageDownloaderDecryptor.m in Sources */ = {isa = PBXBuildFile; fileRef = 3250C9ED2355D9DA0093A896 /* SDWebImageDownloaderDecryptor.m */; };
7275
3250C9F02355D9DA0093A896 /* SDWebImageDownloaderDecryptor.m in Sources */ = {isa = PBXBuildFile; fileRef = 3250C9ED2355D9DA0093A896 /* SDWebImageDownloaderDecryptor.m */; };
@@ -317,6 +320,7 @@
317320
dstPath = include/SDWebImage;
318321
dstSubfolderSpec = 16;
319322
files = (
323+
325074F2296C546D00B730CF /* SDCallbackQueue.h in Copy Headers */,
320324
32D9EE4B24AF259B00EAFDF4 /* SDImageAWebPCoder.h in Copy Headers */,
321325
328E9DE523A61DD30051C893 /* SDGraphicsImageRenderer.h in Copy Headers */,
322326
325F7CCD2389467800AEDFCC /* UIImage+ExtendedCacheData.h in Copy Headers */,
@@ -387,6 +391,8 @@
387391
320224BA203979BA00E9F285 /* SDAnimatedImageRep.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SDAnimatedImageRep.m; path = Core/SDAnimatedImageRep.m; sourceTree = "<group>"; };
388392
320CAE132086F50500CFFC80 /* SDWebImageError.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SDWebImageError.h; path = Core/SDWebImageError.h; sourceTree = "<group>"; };
389393
320CAE142086F50500CFFC80 /* SDWebImageError.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SDWebImageError.m; path = Core/SDWebImageError.m; sourceTree = "<group>"; };
394+
321117A7296573680001FC2C /* SDCallbackQueue.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SDCallbackQueue.h; path = Core/SDCallbackQueue.h; sourceTree = "<group>"; };
395+
321117A8296573680001FC2C /* SDCallbackQueue.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SDCallbackQueue.m; path = Core/SDCallbackQueue.m; sourceTree = "<group>"; };
390396
321B377D2083290D00C0EA77 /* SDImageLoader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SDImageLoader.h; path = Core/SDImageLoader.h; sourceTree = "<group>"; };
391397
321B377E2083290D00C0EA77 /* SDImageLoader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SDImageLoader.m; path = Core/SDImageLoader.m; sourceTree = "<group>"; };
392398
321B377F2083290E00C0EA77 /* SDImageLoadersManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SDImageLoadersManager.h; path = Core/SDImageLoadersManager.h; sourceTree = "<group>"; };
@@ -872,6 +878,8 @@
872878
325312C7200F09910046BF1E /* SDWebImageTransition.m */,
873879
32C0FDDF2013426C001B8F2D /* SDWebImageIndicator.h */,
874880
32C0FDE02013426C001B8F2D /* SDWebImageIndicator.m */,
881+
321117A7296573680001FC2C /* SDCallbackQueue.h */,
882+
321117A8296573680001FC2C /* SDCallbackQueue.m */,
875883
);
876884
name = Utils;
877885
sourceTree = "<group>";
@@ -957,6 +965,7 @@
957965
3250C9EE2355D9DA0093A896 /* SDWebImageDownloaderDecryptor.h in Headers */,
958966
32F7C0862030719600873181 /* UIImage+Transform.h in Headers */,
959967
321E60C01F38E91700405457 /* UIImage+ForceDecode.h in Headers */,
968+
321117A9296573680001FC2C /* SDCallbackQueue.h in Headers */,
960969
329F1243223FAD3400B309FD /* SDInternalMacros.h in Headers */,
961970
80B6DF7F2142B43300BCB334 /* NSImage+Compatibility.h in Headers */,
962971
32C0FDE32013426C001B8F2D /* SDWebImageIndicator.h in Headers */,
@@ -1205,6 +1214,7 @@
12051214
4A2CAE221AB4BB7000B6BC39 /* SDWebImageManager.m in Sources */,
12061215
4A2CAE191AB4BB6400B6BC39 /* SDWebImageCompat.m in Sources */,
12071216
325C460B22339426004CAE11 /* SDWeakProxy.m in Sources */,
1217+
321117AA296573680001FC2C /* SDCallbackQueue.m in Sources */,
12081218
321B37892083290E00C0EA77 /* SDImageLoader.m in Sources */,
12091219
32484771201775F600AF9E5A /* SDAnimatedImage.m in Sources */,
12101220
807A12301F89636300EC2A9B /* SDImageCodersManager.m in Sources */,

SDWebImage/Core/SDCallbackQueue.h

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/*
2+
* This file is part of the SDWebImage package.
3+
* (c) Olivier Poitrey <[email protected]>
4+
*
5+
* For the full copyright and license information, please view the LICENSE
6+
* file that was distributed with this source code.
7+
*/
8+
9+
10+
#import "SDWebImageCompat.h"
11+
12+
/// 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.
13+
typedef NS_ENUM(NSUInteger, SDCallbackPolicy) {
14+
/// 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
15+
SDCallbackPolicySafeExecute = 0,
16+
/// Follow async/sync using the correspond `dispatch_async`/`dispatch_sync` to dispatch block on queue
17+
SDCallbackPolicyDispatch = 1,
18+
/// Ignore any async/sync and just directly invoke `block` in current queue (without `dispatch_async`/`dispatch_sync`)
19+
SDCallbackPolicyInvoke = 2
20+
};
21+
22+
/// SDCallbackQueue is a wrapper used to control how the completionBlock should perform on queues, used by our `Cache`/`Manager`/`Loader`.
23+
/// Useful when you call SDWebImage in non-main queue and want to avoid it callback into main queue, which may cause issue.
24+
@interface SDCallbackQueue : NSObject
25+
26+
/// The shared main queue. This is the default value, has the same effect when passing `nil` to `SDWebImageContextCallbackQueue`
27+
@property (nonnull, class, readonly) SDCallbackQueue *mainQueue;
28+
29+
/// The caller current queue. Using `dispatch_get_current_queue`. This is not a dynamic value and only keep the first call time queue.
30+
@property (nonnull, class, readonly) SDCallbackQueue *currentQueue;
31+
32+
/// The global concurrent queue (user-initiated QoS). Using `dispatch_get_global_queue`.
33+
@property (nonnull, class, readonly) SDCallbackQueue *globalQueue;
34+
35+
/// The current queue's callback policy, defaults to `SDCallbackPolicySafeExecute`, which behaves like the old macro `dispatch_main_async_safe`
36+
@property (assign, readwrite) SDCallbackPolicy policy;
37+
38+
- (nonnull instancetype)init NS_UNAVAILABLE;
39+
+ (nonnull instancetype)new NS_UNAVAILABLE;
40+
/// Create the callback queue with a GCD queue
41+
/// - Parameter queue: The GCD queue, should not be NULL
42+
- (nonnull instancetype)initWithDispatchQueue:(nonnull dispatch_queue_t)queue NS_DESIGNATED_INITIALIZER;
43+
44+
#pragma mark - Execution Entry
45+
46+
/// Submits a block for execution and returns after that block finishes executing.
47+
/// - Parameter block: The block that contains the work to perform.
48+
- (void)sync:(nonnull NS_NOESCAPE dispatch_block_t)block;
49+
50+
/// Schedules a block asynchronously for execution.
51+
/// - Parameter block: The block that contains the work to perform.
52+
- (void)async:(nonnull NS_NOESCAPE dispatch_block_t)block;
53+
54+
@end

SDWebImage/Core/SDCallbackQueue.m

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
/*
2+
* This file is part of the SDWebImage package.
3+
* (c) Olivier Poitrey <[email protected]>
4+
*
5+
* For the full copyright and license information, please view the LICENSE
6+
* file that was distributed with this source code.
7+
*/
8+
9+
10+
#import "SDCallbackQueue.h"
11+
12+
@interface SDCallbackQueue ()
13+
14+
@property (nonatomic, strong, nonnull) dispatch_queue_t queue;
15+
16+
@end
17+
18+
static void * SDCallbackQueueKey = &SDCallbackQueueKey;
19+
static void SDReleaseBlock(void *context) {
20+
CFRelease(context);
21+
}
22+
23+
static void inline SDSafeExecute(dispatch_queue_t _Nonnull queue, dispatch_block_t _Nonnull block, BOOL async) {
24+
// Special handle for main queue label only (custom queue can have the same label)
25+
const char *label = dispatch_queue_get_label(queue);
26+
if (label && label == dispatch_queue_get_label(dispatch_get_main_queue())) {
27+
const char *currentLabel = dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL);
28+
if (label == currentLabel) {
29+
block();
30+
return;
31+
}
32+
}
33+
// Check specific to detect queue equal
34+
void *specific = dispatch_queue_get_specific(queue, SDCallbackQueueKey);
35+
void *currentSpecific = dispatch_get_specific(SDCallbackQueueKey);
36+
if (specific && currentSpecific && CFGetTypeID(specific) == CFUUIDGetTypeID() && CFGetTypeID(currentSpecific) == CFUUIDGetTypeID() && CFEqual(specific, currentSpecific)) {
37+
block();
38+
} else {
39+
if (async) {
40+
dispatch_async(queue, block);
41+
} else {
42+
dispatch_sync(queue, block);
43+
}
44+
}
45+
}
46+
47+
@implementation SDCallbackQueue
48+
49+
- (instancetype)initWithDispatchQueue:(dispatch_queue_t)queue {
50+
self = [super init];
51+
if (self) {
52+
NSCParameterAssert(queue);
53+
CFUUIDRef UUID = CFUUIDCreate(kCFAllocatorDefault);
54+
dispatch_queue_set_specific(queue, SDCallbackQueueKey, (void *)UUID, SDReleaseBlock);
55+
_queue = queue;
56+
}
57+
return self;
58+
}
59+
60+
+ (SDCallbackQueue *)mainQueue {
61+
static dispatch_once_t onceToken;
62+
static SDCallbackQueue *queue;
63+
dispatch_once(&onceToken, ^{
64+
queue = [[SDCallbackQueue alloc] initWithDispatchQueue:dispatch_get_main_queue()];
65+
});
66+
return queue;
67+
}
68+
69+
+ (SDCallbackQueue *)currentQueue {
70+
#pragma clang diagnostic push
71+
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
72+
SDCallbackQueue *queue = [[SDCallbackQueue alloc] initWithDispatchQueue:dispatch_get_current_queue()];
73+
#pragma clang diagnostic pop
74+
return queue;
75+
}
76+
77+
+ (SDCallbackQueue *)globalQueue {
78+
SDCallbackQueue *queue = [[SDCallbackQueue alloc] initWithDispatchQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)];
79+
return queue;
80+
}
81+
82+
- (void)sync:(nonnull NS_NOESCAPE dispatch_block_t)block {
83+
switch (self.policy) {
84+
case SDCallbackPolicySafeExecute:
85+
SDSafeExecute(self.queue, block, NO);
86+
break;
87+
case SDCallbackPolicyDispatch:
88+
dispatch_sync(self.queue, block);
89+
break;
90+
case SDCallbackPolicyInvoke:
91+
block();
92+
break;
93+
}
94+
}
95+
96+
- (void)async:(nonnull NS_NOESCAPE dispatch_block_t)block {
97+
switch (self.policy) {
98+
case SDCallbackPolicySafeExecute:
99+
SDSafeExecute(self.queue, block, YES);
100+
break;
101+
case SDCallbackPolicyDispatch:
102+
dispatch_async(self.queue, block);
103+
break;
104+
case SDCallbackPolicyInvoke:
105+
block();
106+
break;
107+
}
108+
}
109+
110+
@end

SDWebImage/Core/SDImageCache.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,28 @@ typedef NS_OPTIONS(NSUInteger, SDImageCacheOptions) {
225225
toDisk:(BOOL)toDisk
226226
completion:(nullable SDWebImageNoParamsBlock)completionBlock;
227227

228+
/**
229+
* Asynchronously store an image into memory and disk cache at the given key.
230+
*
231+
* @param image The image to store
232+
* @param imageData The image data as returned by the server, this representation will be used for disk storage
233+
* instead of converting the given image object into a storable/compressed image format in order
234+
* to save quality and CPU
235+
* @param key The unique image cache key, usually it's image absolute URL
236+
* @param options A mask to specify options to use for this store
237+
* @param context The context options to use. Pass `.callbackQueue` to control callback queue
238+
* @param cacheType The image store op cache type
239+
* @param completionBlock A block executed after the operation is finished
240+
* @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.
241+
*/
242+
- (void)storeImage:(nullable UIImage *)image
243+
imageData:(nullable NSData *)imageData
244+
forKey:(nullable NSString *)key
245+
options:(SDWebImageOptions)options
246+
context:(nullable SDWebImageContext *)context
247+
cacheType:(SDImageCacheType)cacheType
248+
completion:(nullable SDWebImageNoParamsBlock)completionBlock;
249+
228250
/**
229251
* Synchronously store an image into memory cache at the given key.
230252
*

0 commit comments

Comments
 (0)