Fix the issue that SDWebImagePrefetch in 5.x, will submit all prefetch URLs to manager without any limit, which cause the disk cache query pending until all finished

Because they are implementation details, we can not always assume the cache and downloader use a load balancing algorithm
This commit is contained in:
DreamPiggy 2019-03-03 16:52:44 +08:00
parent 537affb9af
commit 2ce3b706c8
5 changed files with 145 additions and 33 deletions

View File

@ -87,6 +87,10 @@
3290FA061FA478AF0047D20C /* SDImageFrame.h in Headers */ = {isa = PBXBuildFile; fileRef = 3290FA021FA478AF0047D20C /* SDImageFrame.h */; settings = {ATTRIBUTES = (Public, ); }; };
3290FA0A1FA478AF0047D20C /* SDImageFrame.m in Sources */ = {isa = PBXBuildFile; fileRef = 3290FA031FA478AF0047D20C /* SDImageFrame.m */; };
3290FA0C1FA478AF0047D20C /* SDImageFrame.m in Sources */ = {isa = PBXBuildFile; fileRef = 3290FA031FA478AF0047D20C /* SDImageFrame.m */; };
3299C4EE222BC3FD00DABA0F /* SDAsyncBlockOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = 3299C4EC222BC3FD00DABA0F /* SDAsyncBlockOperation.h */; settings = {ATTRIBUTES = (Public, ); }; };
3299C4EF222BC3FD00DABA0F /* SDAsyncBlockOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = 3299C4EC222BC3FD00DABA0F /* SDAsyncBlockOperation.h */; settings = {ATTRIBUTES = (Public, ); }; };
3299C4F0222BC3FD00DABA0F /* SDAsyncBlockOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 3299C4ED222BC3FD00DABA0F /* SDAsyncBlockOperation.m */; };
3299C4F1222BC3FD00DABA0F /* SDAsyncBlockOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 3299C4ED222BC3FD00DABA0F /* SDAsyncBlockOperation.m */; };
329A18591FFF5DFD008C9A2F /* UIImage+Metadata.h in Headers */ = {isa = PBXBuildFile; fileRef = 329A18571FFF5DFD008C9A2F /* UIImage+Metadata.h */; settings = {ATTRIBUTES = (Public, ); }; };
329A185B1FFF5DFD008C9A2F /* UIImage+Metadata.h in Headers */ = {isa = PBXBuildFile; fileRef = 329A18571FFF5DFD008C9A2F /* UIImage+Metadata.h */; settings = {ATTRIBUTES = (Public, ); }; };
329A185F1FFF5DFD008C9A2F /* UIImage+Metadata.m in Sources */ = {isa = PBXBuildFile; fileRef = 329A18581FFF5DFD008C9A2F /* UIImage+Metadata.m */; };
@ -267,6 +271,8 @@
328BB6C02082581100760D6C /* SDMemoryCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDMemoryCache.m; sourceTree = "<group>"; };
3290FA021FA478AF0047D20C /* SDImageFrame.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SDImageFrame.h; sourceTree = "<group>"; };
3290FA031FA478AF0047D20C /* SDImageFrame.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SDImageFrame.m; sourceTree = "<group>"; };
3299C4EC222BC3FD00DABA0F /* SDAsyncBlockOperation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SDAsyncBlockOperation.h; sourceTree = "<group>"; };
3299C4ED222BC3FD00DABA0F /* SDAsyncBlockOperation.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SDAsyncBlockOperation.m; sourceTree = "<group>"; };
329A18571FFF5DFD008C9A2F /* UIImage+Metadata.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIImage+Metadata.h"; sourceTree = "<group>"; };
329A18581FFF5DFD008C9A2F /* UIImage+Metadata.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIImage+Metadata.m"; sourceTree = "<group>"; };
32B9B535206ED4230026769D /* SDWebImageDownloaderConfig.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SDWebImageDownloaderConfig.h; sourceTree = "<group>"; };
@ -425,6 +431,8 @@
children = (
53922D91148C56230056699D /* SDWebImagePrefetcher.h */,
53922D92148C56230056699D /* SDWebImagePrefetcher.m */,
3299C4EC222BC3FD00DABA0F /* SDAsyncBlockOperation.h */,
3299C4ED222BC3FD00DABA0F /* SDAsyncBlockOperation.m */,
);
name = Prefetcher;
sourceTree = "<group>";
@ -633,6 +641,7 @@
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
3299C4EF222BC3FD00DABA0F /* SDAsyncBlockOperation.h in Headers */,
32D122202080B2EB003685A3 /* SDImageCacheDefine.h in Headers */,
32B9B539206ED4230026769D /* SDWebImageDownloaderConfig.h in Headers */,
3257EAFA21898AED0097B271 /* SDImageGraphics.h in Headers */,
@ -690,6 +699,7 @@
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
3299C4EE222BC3FD00DABA0F /* SDAsyncBlockOperation.h in Headers */,
32CF1C071FA496B000004BD1 /* SDImageCoderHelper.h in Headers */,
32F7C0842030719600873181 /* UIImage+Transform.h in Headers */,
3257EAF921898AED0097B271 /* SDImageGraphics.h in Headers */,
@ -896,6 +906,7 @@
32F7C0772030114C00873181 /* SDImageTransformer.m in Sources */,
3237F9E820161AE000A88143 /* NSImage+Compatibility.m in Sources */,
32C0FDE92013426C001B8F2D /* SDWebImageIndicator.m in Sources */,
3299C4F1222BC3FD00DABA0F /* SDAsyncBlockOperation.m in Sources */,
32F21B5920788D8C0036B1D5 /* SDWebImageDownloaderRequestModifier.m in Sources */,
321B37952083290E00C0EA77 /* SDImageLoadersManager.m in Sources */,
4A2CAE361AB4BB7500B6BC39 /* UIImageView+WebCache.m in Sources */,
@ -951,6 +962,7 @@
32F7C0752030114C00873181 /* SDImageTransformer.m in Sources */,
3237F9EB20161AE000A88143 /* NSImage+Compatibility.m in Sources */,
32C0FDE72013426C001B8F2D /* SDWebImageIndicator.m in Sources */,
3299C4F0222BC3FD00DABA0F /* SDAsyncBlockOperation.m in Sources */,
32F21B5720788D8C0036B1D5 /* SDWebImageDownloaderRequestModifier.m in Sources */,
5376130B155AD0D5005750A4 /* SDWebImageDownloader.m in Sources */,
321B37932083290E00C0EA77 /* SDImageLoadersManager.m in Sources */,

View File

@ -0,0 +1,20 @@
/*
* This file is part of the SDWebImage package.
* (c) Olivier Poitrey <rs@dailymotion.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
#import "SDWebImageCompat.h"
@class SDAsyncBlockOperation;
typedef void (^SDAsyncBlock)(SDAsyncBlockOperation * __nonnull asyncOperation);
@interface SDAsyncBlockOperation : NSOperation
- (nonnull instancetype)initWithBlock:(nonnull SDAsyncBlock)block;
+ (nonnull instancetype)blockOperationWithBlock:(nonnull SDAsyncBlock)block;
- (void)complete;
@end

View File

@ -0,0 +1,55 @@
/*
* This file is part of the SDWebImage package.
* (c) Olivier Poitrey <rs@dailymotion.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
#import "SDAsyncBlockOperation.h"
@interface SDAsyncBlockOperation ()
@property (nonatomic, assign) BOOL isExecuting;
@property (nonatomic, assign) BOOL isFinished;
@property (nonatomic, copy, nonnull) SDAsyncBlock executionBlock;
@end
@implementation SDAsyncBlockOperation
- (nonnull instancetype)initWithBlock:(nonnull SDAsyncBlock)block {
self = [super init];
if (self) {
self.executionBlock = block;
}
return self;
}
+ (nonnull instancetype)blockOperationWithBlock:(nonnull SDAsyncBlock)block {
SDAsyncBlockOperation *operation = [[SDAsyncBlockOperation alloc] initWithBlock:block];
return operation;
}
- (void)start {
[self willChangeValueForKey:@"isExecuting"];
self.isExecuting = YES;
[self didChangeValueForKey:@"isExecuting"];
if (self.executionBlock) {
self.executionBlock(self);
} else {
[self complete];
}
}
- (void)complete {
[self willChangeValueForKey:@"isExecuting"];
[self willChangeValueForKey:@"isFinished"];
self.isExecuting = NO;
self.isFinished = YES;
[self didChangeValueForKey:@"isExecuting"];
[self didChangeValueForKey:@"isFinished"];
}
@end

View File

@ -63,6 +63,11 @@ typedef void(^SDWebImagePrefetcherCompletionBlock)(NSUInteger noOfFinishedUrls,
*/
@property (strong, nonatomic, readonly, nonnull) SDWebImageManager *manager;
/**
* Maximum number of URLs to prefetch at the same time. Defaults to 3.
*/
@property (nonatomic, assign) NSUInteger maxConcurrentPrefetchCount;
/**
* The options for prefetcher. Defaults to SDWebImageLowPriority.
*/

View File

@ -7,6 +7,7 @@
*/
#import "SDWebImagePrefetcher.h"
#import "SDAsyncBlockOperation.h"
#import <stdatomic.h>
@interface SDWebImagePrefetchToken () {
@ -32,6 +33,7 @@
@property (strong, nonatomic, nonnull) SDWebImageManager *manager;
@property (strong, atomic, nonnull) NSMutableSet<SDWebImagePrefetchToken *> *runningTokens;
@property (strong, nonatomic, nonnull) NSOperationQueue *prefetchQueue;
@end
@ -56,10 +58,20 @@
_runningTokens = [NSMutableSet set];
_options = SDWebImageLowPriority;
_delegateQueue = dispatch_get_main_queue();
_prefetchQueue = [NSOperationQueue new];
self.maxConcurrentPrefetchCount = 3;
}
return self;
}
- (void)setMaxConcurrentPrefetchCount:(NSUInteger)maxConcurrentPrefetchCount {
self.prefetchQueue.maxConcurrentOperationCount = maxConcurrentPrefetchCount;
}
- (NSUInteger)maxConcurrentPrefetchCount {
return self.prefetchQueue.maxConcurrentOperationCount;
}
#pragma mark - Prefetch
- (nullable SDWebImagePrefetchToken *)prefetchURLs:(nullable NSArray<NSURL *> *)urls {
return [self prefetchURLs:urls progress:nil completed:nil];
@ -85,43 +97,51 @@
token.progressBlock = progressBlock;
token.completionBlock = completionBlock;
[self addRunningToken:token];
NSPointerArray *operations = token.operations;
for (NSURL *url in urls) {
__weak typeof(self) wself = self;
id<SDWebImageOperation> operation = [self.manager loadImageWithURL:url options:self.options context:self.context progress:nil completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, SDImageCacheType cacheType, BOOL finished, NSURL * _Nullable imageURL) {
__strong typeof(wself) sself = wself;
if (!sself) {
return;
}
if (!finished) {
return;
}
atomic_fetch_add_explicit(&(token->_finishedCount), 1, memory_order_relaxed);
if (error) {
// Add last failed
atomic_fetch_add_explicit(&(token->_skippedCount), 1, memory_order_relaxed);
}
// Current operation finished
[sself callProgressBlockForToken:token imageURL:imageURL];
if (atomic_load_explicit(&(token->_finishedCount), memory_order_relaxed) == token->_totalCount) {
// All finished
if (!atomic_flag_test_and_set_explicit(&(token->_isAllFinished), memory_order_relaxed)) {
[sself callCompletionBlockForToken:token];
[sself removeRunningToken:token];
}
}
}];
@synchronized (token) {
[operations addPointer:(__bridge void *)operation];
}
}
[self startPrefetchWithToken:token];
return token;
}
- (void)startPrefetchWithToken:(SDWebImagePrefetchToken * _Nonnull)token {
NSPointerArray *operations = token.operations;
for (NSURL *url in token.urls) {
__weak typeof(self) wself = self;
SDAsyncBlockOperation *prefetchOperation = [SDAsyncBlockOperation blockOperationWithBlock:^(SDAsyncBlockOperation * _Nonnull asyncOperation) {
id<SDWebImageOperation> operation = [self.manager loadImageWithURL:url options:self.options context:self.context progress:nil completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, SDImageCacheType cacheType, BOOL finished, NSURL * _Nullable imageURL) {
__strong typeof(wself) sself = wself;
if (!sself) {
return;
}
if (!finished) {
return;
}
[asyncOperation complete];
atomic_fetch_add_explicit(&(token->_finishedCount), 1, memory_order_relaxed);
if (error) {
// Add last failed
atomic_fetch_add_explicit(&(token->_skippedCount), 1, memory_order_relaxed);
}
// Current operation finished
[sself callProgressBlockForToken:token imageURL:imageURL];
if (atomic_load_explicit(&(token->_finishedCount), memory_order_relaxed) == token->_totalCount) {
// All finished
if (!atomic_flag_test_and_set_explicit(&(token->_isAllFinished), memory_order_relaxed)) {
[sself callCompletionBlockForToken:token];
[sself removeRunningToken:token];
}
}
}];
@synchronized (token) {
[operations addPointer:(__bridge void *)operation];
}
}];
[self.prefetchQueue addOperation:prefetchOperation];
}
}
#pragma mark - Cancel
- (void)cancelPrefetching {
@synchronized(self.runningTokens) {