Revert back the NSOperationQueue, seems has better performance on this cases (may dispatch multiple block but not important, one finished is OK)

This commit is contained in:
DreamPiggy 2023-02-06 16:09:07 +08:00
parent 6e1fee7834
commit 0c94e540bc
4 changed files with 40 additions and 25 deletions

View File

@ -90,6 +90,7 @@
if (!_fetchQueue) {
_fetchQueue = [[NSOperationQueue alloc] init];
_fetchQueue.maxConcurrentOperationCount = 1;
_fetchQueue.name = @"com.hackemist.SDAnimatedImagePlayer.fetchQueue";
}
return _fetchQueue;
}

View File

@ -118,7 +118,7 @@ static NSString * _defaultDiskCacheDirectory;
// Create IO queue
dispatch_queue_attr_t ioQueueAttributes = _config.ioQueueAttributes;
_ioQueue = dispatch_queue_create("com.hackemist.SDImageCache", ioQueueAttributes);
_ioQueue = dispatch_queue_create("com.hackemist.SDImageCache.ioQueue", ioQueueAttributes);
NSAssert(_ioQueue, @"The IO queue should not be nil. Your configured `ioQueueAttributes` may be wrong");
// Init the memory cache

View File

@ -116,7 +116,7 @@ void SDWebImageDownloaderOperationSetCompleted(id<SDWebImageDownloaderOperation>
[_config addObserver:self forKeyPath:NSStringFromSelector(@selector(maxConcurrentDownloads)) options:0 context:SDWebImageDownloaderContext];
_downloadQueue = [NSOperationQueue new];
_downloadQueue.maxConcurrentOperationCount = _config.maxConcurrentDownloads;
_downloadQueue.name = @"com.hackemist.SDWebImageDownloader";
_downloadQueue.name = @"com.hackemist.SDWebImageDownloader.downloadQueue";
_URLOperations = [NSMutableDictionary new];
NSMutableDictionary<NSString *, NSString *> *headerDictionary = [NSMutableDictionary dictionary];
NSString *userAgent = nil;

View File

@ -75,7 +75,7 @@ BOOL SDWebImageDownloaderOperationGetCompleted(id<SDWebImageDownloaderOperation>
@property (strong, nonatomic, readwrite, nullable) NSURLSessionTaskMetrics *metrics API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0));
@property (strong, nonatomic, nonnull) dispatch_queue_t coderQueue; // the serial operation queue to do image decoding
@property (strong, nonatomic, nonnull) NSOperationQueue *coderQueue; // the serial operation queue to do image decoding
@property (strong, nonatomic, nonnull) NSMapTable<SDImageCoderOptions *, UIImage *> *imageMap; // each variant of image is weak-referenced to avoid too many re-decode during downloading
#if SD_UIKIT
@ -112,7 +112,9 @@ BOOL SDWebImageDownloaderOperationGetCompleted(id<SDWebImageDownloaderOperation>
_finished = NO;
_expectedSize = 0;
_unownedSession = session;
_coderQueue = dispatch_queue_create("com.hackemist.SDWebImageDownloaderOperation", DISPATCH_QUEUE_SERIAL);
_coderQueue = [[NSOperationQueue alloc] init];
_coderQueue.maxConcurrentOperationCount = 1;
_coderQueue.name = @"com.hackemist.SDWebImageDownloaderOperation.coderQueue";
_imageMap = [[NSMapTable alloc] initWithKeyOptions:NSPointerFunctionsStrongMemory valueOptions:NSPointerFunctionsWeakMemory capacity:1];
#if SD_UIKIT
_backgroundTaskId = UIBackgroundTaskInvalid;
@ -466,23 +468,26 @@ didReceiveResponse:(NSURLResponse *)response
NSData *imageData = self.imageData;
// keep maximum one progressive decode process during download
@weakify(self);
dispatch_async(self.coderQueue, ^{
@strongify(self);
if (!self) {
return;
}
// When cancelled or transfer finished (`didCompleteWithError`), cancel the progress callback, only completed block is called and enough
if (self.isCancelled || SDWebImageDownloaderOperationGetCompleted(self)) {
return;
}
UIImage *image = SDImageLoaderDecodeProgressiveImageData(imageData, self.request.URL, NO, self, [[self class] imageOptionsFromDownloaderOptions:self.options], self.context);
if (image) {
// We do not keep the progressive decoding image even when `finished`=YES. Because they are for view rendering but not take full function from downloader options. And some coders implementation may not keep consistent between progressive decoding and normal decoding.
[self callCompletionBlocksWithImage:image imageData:nil error:nil finished:NO];
}
});
if (self.coderQueue.operationCount == 0) {
// NSOperation have autoreleasepool, don't need to create extra one
@weakify(self);
[self.coderQueue addOperationWithBlock:^{
@strongify(self);
if (!self) {
return;
}
// When cancelled or transfer finished (`didCompleteWithError`), cancel the progress callback, only completed block is called and enough
if (self.isCancelled || SDWebImageDownloaderOperationGetCompleted(self)) {
return;
}
UIImage *image = SDImageLoaderDecodeProgressiveImageData(imageData, self.request.URL, NO, self, [[self class] imageOptionsFromDownloaderOptions:self.options], self.context);
if (image) {
// We do not keep the progressive decoding image even when `finished`=YES. Because they are for view rendering but not take full function from downloader options. And some coders implementation may not keep consistent between progressive decoding and normal decoding.
[self callCompletionBlocksWithImage:image imageData:nil error:nil finished:NO];
}
}];
}
}
for (SDWebImageDownloaderOperationToken *token in tokens) {
@ -556,9 +561,11 @@ didReceiveResponse:(NSURLResponse *)response
[self callCompletionBlocksWithError:self.responseError];
[self done];
} else {
// decode the image in coder queue, cancel all previous decoding process
[self.coderQueue cancelAllOperations];
@weakify(self);
for (SDWebImageDownloaderOperationToken *token in tokens) {
dispatch_async(self.coderQueue, ^{
[self.coderQueue addOperationWithBlock:^{
@strongify(self);
if (!self) {
return;
@ -596,16 +603,23 @@ didReceiveResponse:(NSURLResponse *)response
} else {
[self callCompletionBlockWithToken:token image:image imageData:imageData error:nil finished:YES];
}
});
}];
}
// call [self done] after all completed block was dispatched
dispatch_async(self.coderQueue, ^{
dispatch_block_t doneBlock = ^{
@strongify(self);
if (!self) {
return;
}
[self done];
});
};
if (@available(iOS 13, tvOS 13, macOS 10.15, watchOS 6, *)) {
// seems faster than `addOperationWithBlock`
[self.coderQueue addBarrierBlock:doneBlock];
} else {
// serial queue, this does the same effect in semantics
[self.coderQueue addOperationWithBlock:doneBlock];
}
}
} else {
[self callCompletionBlocksWithError:[NSError errorWithDomain:SDWebImageErrorDomain code:SDWebImageErrorBadImageData userInfo:@{NSLocalizedDescriptionKey : @"Image data is nil"}]];