SDWebImage
库总体分为这么几个部分:
- 类似
UIImageView+WebCache
这样的面向使用者的接口,由各个分类的sd_setImageWithURL:...
系列方法组成,是我们最常接触使用的部分。 -
SDImageCache
,是SDWebImage
库中负责缓存工作的类。 -
SDImageDownloader
,是SDWebImage
库中负责下载工作的类。 -
SDWebImageManager
,是管理SDImageCache
和SDImageDownloader
,让它们协同工作的类。在各个分类的背后默默工作。
UIView+WebCacheOperation
这个分类中提供了几个方法,可以对operation进行操作。
首先这个分类中,使用Runtime
为UIView
添加一个字典属性(NSMutableDictionary<NSString *, id>
),里面存储了一些下载操作id <SDWebImageOperation> operation
,可能为一个op或者op集合。
删除operation
- (void)sd_removeImageLoadOperationWithKey:(nullable NSString *)key
查找字典中以key
为键的op,并删除。
取消operation
- (void)sd_cancelImageLoadOperationWithKey:(nullable NSString *)key
查找字典中以key
为键的op,调用- (void)cancel
方法。(如果是集合,则遍历调用cancel
)。取消后会将op从字典中删除。
添加operation
- (void)sd_setImageLoadOperation:(nullable id)operation forKey:(nullable NSString *)key
先取消key
对应的op,再将新想op添加到字典中。
UIView+WebCache
这个分类中提供了图片下载、设置的方法,为这个库中UIImageView
分类和UIButton
分类的sd_set...
系列方法提供支持。还提供了显示/隐藏ActivityIndicator
的方法。
- (nullable NSURL *)sd_imageURL;
获取当前视图图片的url,方法通过Runtime
获取,因此如果我们在设置图片时直接使用setImage
而非使用sd中的方法,会使这个方法获取的值错误。
// 取消当前视图的图片下载
- (void)sd_cancelCurrentImageLoad
上面这个方法会以类名为key,调用UIView+WebCacheOperation
中的- (void)sd_cancelImageLoadOperationWithKey:(nullable NSString *)key
方法来取消图片下载操作。
- (void)sd_setImage:(UIImage *)image imageData:(NSData *)imageData basedOnClassOrViaCustomSetImageBlock:(SDSetImageBlock)setImageBlock {
if (setImageBlock) {
setImageBlock(image, imageData);
return;
}
#if SD_UIKIT || SD_MAC
if ([self isKindOfClass:[UIImageView class]]) {
UIImageView *imageView = (UIImageView *)self;
imageView.image = image;
}
#endif
#if SD_UIKIT
if ([self isKindOfClass:[UIButton class]]) {
UIButton *button = (UIButton *)self;
[button setImage:image forState:UIControlStateNormal];
}
#endif
}
内部方法设置图片,如果有setImageBlock则调用它来设置图片,否则的话UIImageView
调用setImage:
方法,UIButton
调用setImage:forState:
方法来设置图片。
- (void)sd_internalSetImageWithURL:(nullable NSURL *)url
placeholderImage:(nullable UIImage *)placeholder
options:(SDWebImageOptions)options
operationKey:(nullable NSString *)operationKey
setImageBlock:(nullable SDSetImageBlock)setImageBlock
progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
completed:(nullable SDExternalCompletionBlock)completedBlock;
这个方法是这个分类的核心方法,也是UIImageView+WebCache
和UIButton+WebCache
中各个方法设置图片时实际调用的方法。
这个方法中做的主要事情是调用SDWebImageManager
中的方法创建一个operation,并将这个op添加到op字典中。
方法中会在创建operation前使用上面内部方法设置placeholder图片,如果设置了SDWebImageDelayPlaceholder
,则会跳过,最后在operation回调中,如果下载图片失败再设置placeholder。
方法中对op的complete回调进行了处理,如果设置了SDWebImageAvoidAutoSetImage
,会直接调用completeBlock
,否则的话会使用下载的图片或者占位图作为参数,调用setImageBlock
, 最后调用completeBlock
。
这两个block的调用都是通过调用上面提到的内部方法sd_setImage:imageData:basedOnClassOrViaCustomSetImageBlock:
。
实际在UIImageView+WebCache
中调用这个方法时,setImageBlock
是nil,除非调用的是带completeBlock
的方法并设置了block值,否则completBlock
也会传入nil,最终将使用imageView.image = image
这样的方法设置图片,而不是由外部block设置。
UIButton+WebCache
中调用时,setImageBlock
会有值,其内容是调用UIButton
的setImage:forState:
或者setBackgroundImage:forState:
方法。completBlock
除非设置否则为空。
UIImageView+WebCache
这个分类中提供了一系列sd_setImage...
方法供外部调用,是使用者最经常接触的API,这系列方法最终都是调用到了UIView+WebCache
中的核心方法。
另外,UIImageView
是支持多图动画的,这个分类也提供了相应的方法:
- (void)sd_setAnimationImagesWithURLs:(nonnull NSArray<NSURL *> *)arrayOfURLs {
[self sd_cancelCurrentAnimationImagesLoad];
__weak __typeof(self)wself = self;
NSMutableArray<id<SDWebImageOperation>> *operationsArray = [[NSMutableArray alloc] init];
for (NSURL *logoImageURL in arrayOfURLs) {
id <SDWebImageOperation> operation = [SDWebImageManager.sharedManager loadImageWithURL:logoImageURL options:0 progress:nil completed:^(UIImage *image, NSData *data, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
if (!wself) return;
dispatch_main_async_safe(^{
__strong UIImageView *sself = wself;
[sself stopAnimating];
if (sself && image) {
NSMutableArray<UIImage *> *currentImages = [[sself animationImages] mutableCopy];
if (!currentImages) {
currentImages = [[NSMutableArray alloc] init];
}
[currentImages addObject:image];
sself.animationImages = currentImages;
[sself setNeedsLayout];
}
[sself startAnimating];
});
}];
[operationsArray addObject:operation];
}
[self sd_setImageLoadOperation:[operationsArray copy] forKey:@"UIImageViewAnimationImages"];
}
- (void)sd_cancelCurrentAnimationImagesLoad {
[self sd_cancelImageLoadOperationWithKey:@"UIImageViewAnimationImages"];
}
使用多个url来创建多个operation,再将这些op放入数组,最后将op数组添加到op字典中。添加op和取消下载都单独提供了一个key与正常的一张图片的操作区分。
UIImageView+HighlightedWebCache
与此分类类似,唯一不同的是,它会提供核心方法参数中的setImageBlock
,其内容就是设置highlightedImage
。
UIButton+HighlightedWebCache
与此分类类似,唯一不同的是,它会提供核心方法参数中的setImageBlock
,其内容就是设置setImage:forState:
或者setBackgroundImage:forState:
。
UIImage+GIF
使用NSData创建Gif图片
+ (UIImage *)sd_animatedGIFWithData:(NSData *)data
判断是否是Gif
- (BOOL)isGIF {
return (self.images != nil);
}
SDWebImagePrefetcher
预加载图片,默认在主队列,低优先级加载图片。
可以通过prefetcherQueue
、options
、maxConcurrentDownloads
等对预加载进行控制,可以通过代理的回调获取预加载的状态信息。
SDWebImageOperation
一个协议,里面只定义了一个取消方法。
@protocol SDWebImageOperation <NSObject>
- (void)cancel;
@end
SDImageCacheConfig
图片缓存设置
- shouldDecompressImages,是否解码图片。对下载和缓存的图片解码,可以加快图片的显示,但会消耗更多的内存。默认YES。
- shouldDisableiCloud,是否禁用iCloud备份,默认YES,即禁用。
SharedSDImageCache
默认存储的沙盒路径(Cache)是不会被iCloud备份的,但是SDImageCache可以添加自定义缓存路径,这时候就可能存储在会被iCloud备份的路径上,此时这个选项就有了意义。 - shouldCacheImagesInMemory,是否在内存中缓存图片,默认YES。
- maxCacheAge,最大缓存时间(秒为单位),默认一周。
- maxCacheSize,最大缓存空间(bytes),默认0。
SDImageCache
SDImageCache
图片缓存,它包括内存缓存和可选的硬盘缓存,硬盘缓存写入时是异步的,不会阻塞UI。
- (nonnull instancetype)initWithNamespace:(nonnull NSString *)ns
diskCacheDirectory:(nonnull NSString *)directory
初始化,SD会在沙盒的Cache文件夹下创建命名空间同名的文件夹A,最终以/Sandbox/Library/Caches/namespace/com.hackemist.SDWebImageCache.namespace
作为硬盘缓存路径。
初始化时会注册通知,在程序收到内存警告时清除所有内存缓存,在即将关闭和进入后台时清除过期硬盘缓存。删除过期文件方法中,会优先删除过期文件(默认为保存一周)。
过期文件删除之后计算缓存大小,如果大小超过设置的缓存大小限制,会继续删除,优先删除最旧的图片,删到缓存大小为设置大小一半为止。
会创建一个自定义串行队列做IO操作,所有的存入、删除、查询操作都会在这个队列里完成,以此来保证线程安全。获取图片并没有控制在此队列完成。
sharedImageCache
单例会以default
为命名空间初始化。
内部的AutoPurgeCache
, 继承自NSCache
,在收到内存警告时,会自动删除内存中的所有缓存。SD中的内存缓存属性为NSCache
,实际为使用AutoPurgeCache
初始化的实例。
NSUInteger SDCacheCostForImage(UIImage *image)
, 计算一张图片的缓存大小消耗。在Mac中为wid * hei
, 在iOS中为wid * scale * hei * scale
。
- (void)addReadOnlyCachePath:(nonnull NSString *)path
添加硬盘缓存的搜索路径,可以在想预加载Bundle资源时使用。
- (nullable NSString *)cachedFileNameForKey:(nullable NSString *)key
以key获取硬盘缓存文件的文件名,文件名是将key做MD5加密,如果后后缀名加上文件后缀。
- (void)storeImage:(nullable UIImage *)image
imageData:(nullable NSData *)imageData
forKey:(nullable NSString *)key
toDisk:(BOOL)toDisk
根据参数和配置(SDImageCacheConfig
)将图片缓存到内存和硬盘。
缓存到硬盘时,会控制在初始化时创建的io队列进行操作,实际调用了
- (void)storeImageDataToDisk:(nullable NSData *)imageData forKey:(nullable NSString *)key
这个方法进行存储,这个方法中会检查队列是否是指定的io队列,不会再控制代码在io队列上执行。
如果方法没提供imageData
,会将image转为Data, 最后存入硬盘的是Data。
如果设置中包含了禁用iCloud备份,会在存入时对URL设置禁用iCloud备份。
- (void)diskImageExistsWithKey:(nullable NSString *)key completion:(nullable SDWebImageCheckCacheCompletionBlock)completionBlock
以key获取文件路径,查询硬盘图片是否存在,并不会加载图片。
- (nullable UIImage *)imageFromMemoryCacheForKey:(nullable NSString *)key {
return [self.memCache objectForKey:key];
}
以key查找内存缓存的图片。
- (nullable UIImage *)imageFromDiskCacheForKey:(nullable NSString *)key {
UIImage *diskImage = [self diskImageForKey:key];
if (diskImage && self.config.shouldCacheImagesInMemory) {
NSUInteger cost = SDCacheCostForImage(diskImage);
[self.memCache setObject:diskImage forKey:key cost:cost];
}
return diskImage;
}
// inner method
- (nullable UIImage *)diskImageForKey:(nullable NSString *)key {
// 查找硬盘缓存Data
NSData *data = [self diskImageDataBySearchingAllPathsForKey:key];
if (data) {
UIImage *image = [UIImage sd_imageWithData:data];
// 如果文件名包含@2x、@3x,会以不同的scale创建UIImage
image = [self scaledImageForKey:key image:image];
if (self.config.shouldDecompressImages) {
// 解码图片
image = [UIImage decodedImageWithImage:image];
}
return image;
}
else {
return nil;
}
}
获取硬盘缓存的图片,查找的时候会从所有的路径查找(默认路径+自定义路径),查找到Data后会根据图片种类创建不同的UIImage对象([UIImage sd_imageWithData:]
), 比如Gif、WebP等,并根据文件名是否包含@2x、@3x调整image的scale。
如果设置了预解码,会对图片进行解码,解码操作会过滤调动图和含有alpha通道的图片。
如果设置了内存缓存,会将图片添加到内存缓存中。
- (nullable UIImage *)imageFromCacheForKey:(nullable NSString *)key
查找缓存的图片,先从内存查找,没有再从硬盘查找。
- (nullable NSOperation *)queryCacheOperationForKey:(nullable NSString *)key
done:(nullable SDCacheQueryCompletedBlock)doneBlock
返回一个[NSOperation new]
, 方法会先查找内存缓存,如果找到则以对应的图片、Data、缓存类型等调用done
回调。如果没找到会在io队列查找硬盘缓存。
如果所返回的NSOperation
被取消,则会停止查找硬盘缓存。
- (void)removeImageForKey:(nullable NSString *)key
fromDisk:(BOOL)fromDisk
withCompletion:(nullable SDWebImageNoParamsBlock)completion
从缓存中删除图片。
如果设置了内存缓存,则会从内存缓存中删除图片。
如果fromDisk
为真,则会在io队列中删除硬盘缓存。
回调会在主线程被调用。
- (void)deleteOldFilesWithCompletionBlock:(nullable SDWebImageNoParamsBlock)completionBlock
删除过期文件方法中,会优先删除过期文件(默认为保存一周)。
过期文件删除之后计算缓存大小,如果大小超过设置的缓存大小限制,会继续删除,优先删除最旧的图片,删到缓存大小为设置大小一半为止。
SDWebImageDownloaderOperation
SDWebImageDownloaderOperation
继承自NSOperation
,实现了多个代理协议方法:
-
SDWebImageDownloaderOperationInterface
, 提供创建对象方法;添加下载过程回调、结果回调;设置是否解码图片等。 -
SDWebImageOperation
,提供- (void)cancel
方法。 -
NSURLSessionTaskDelegate
和NSURLSessionDataDelegate
这个类是下载图片的operation对象,它会处理NSURLSessionTaskDelegate
和NSURLSessionDataDelegate
中的方法。
这个类内部有两个NSURLSession
类型属性:
-
unownedSession
,外部提供的session,由外部管理和负责置空,代理和代理方法也由外部控制实现。 -
ownedSession
,当外部提供的session为空时,内部创建的session,自己负责停用和置空,自己实现代理方法。
SD实际代码中,外部session由SDWebImageDownloader
提供和管理,Downloader中实现了相关代理方法,但是在代理方法中会调用SDWebImageDownloaderOperation
实现的相关代理方法来处理逻辑:
// SDWebImageDownloader中
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
SDWebImageDownloaderOperation *dataOperation = [self operationWithTask:task];
// 调用operation实现的代理方法
[dataOperation URLSession:session task:task didCompleteWithError:error];
}
这个类内部维护了一个回调字典,存储了所有添加的progressCallback
和completedCallback
,并创建了一个barrierQueue
,将对回调字典的操作放到这个队列中来保证线程安全。
类里面重写了NSOperation
的- (void)start
和- (void)cancel
方法,结合下载的各个代理方法控制operation的finished
和executing
状态, 并根据NSOperation的文档重写了这两个属性的setter
。
@property (readonly, getter=isExecuting) BOOL executing
A Boolean value indicating whether the operation is currently executing.
The value of this property is YES if the operation is currently executing its main task or NO if it is not.
When implementing a concurrent operation object, you must override the implementation of this property so that you can return the execution state of your operation.
In your custom implementation, you must generate KVO notifications for the isExecuting
key path whenever the execution state of your operation object changes. For more information about manually generating KVO notifications, see Key-Value Observing Programming Guide.
You do not need to reimplement this property for nonconcurrent operations.
finished
与之相同。
如果实现的operation是并发的,必须重写这两个属性,来自己控制op的状态,并且需要在属性改变时给出KVO通知。如果不是并发的,则没必要重写。
@implementation SDWebImageDownloaderOperation {
@synthesize executing = _executing;
@synthesize finished = _finished;
- (void)setFinished:(BOOL)finished {
[self willChangeValueForKey:@"isFinished"];
_finished = finished;
[self didChangeValueForKey:@"isFinished"];
}
- (void)setExecuting:(BOOL)executing {
[self willChangeValueForKey:@"isExecuting"];
_executing = executing;
[self didChangeValueForKey:@"isExecuting"];
}
}
重写父类属性时,需要使用到成员变量,则要用@synthesize
,否则在子类重写的setter
和getter
中是访问不到的。
SDWebImageDownloader
SD中的图片下载器,与SDWebImageCache
构成这个库中的“下载”和“缓存”两大核心。
下载选项
typedef NS_OPTIONS(NSUInteger, SDWebImageDownloaderOptions) {
// 默认,影响下载操作在队列中的优先级
SDWebImageDownloaderLowPriority = 1 << 0,
// 渐进式下载,可以逐步显示出图片全部
SDWebImageDownloaderProgressiveDownload = 1 << 1,
/**
* 默认情况下下载图片请求会设置NSURLRequestReloadIgnoringLocalCacheData缓存策略,不使用NSURLCache缓存。
* 如果设置了这个选项,会使用NSURLRequestUseProtocolCachePolicy,启动NSURLCache缓存。
*/
SDWebImageDownloaderUseNSURLCache = 1 << 2,
/**
* 忽略从NSURLCache缓存的图片,如果图片是从NSURLCache获取到,则以nil来回调
* 可以结合`SDWebImageDownloaderUseNSURLCache`一起使用。
*/
SDWebImageDownloaderIgnoreCachedResponse = 1 << 3,
// 在App进入后台继续下载图片
SDWebImageDownloaderContinueInBackground = 1 << 4,
// 影响NSMutableURLRequest.HTTPShouldHandleCookies属性值
SDWebImageDownloaderHandleCookies = 1 << 5,
// 允许不被信任的SSL证书
SDWebImageDownloaderAllowInvalidSSLCertificates = 1 << 6,
// 高优先级下载图片
SDWebImageDownloaderHighPriority = 1 << 7,
// 默认情况下,硬盘缓存的Data是在请求中逐渐拼接起来的NSMutableData
// 如果设置此项,则使用UIImagePNGRepresentation(image), 此Data会比前者更小
SDWebImageDownloaderScaleDownLargeImages = 1 << 8,
};
下载顺序
typedef NS_ENUM(NSInteger, SDWebImageDownloaderExecutionOrder) {
// 默认值,先进先出
SDWebImageDownloaderFIFOExecutionOrder,
// 后进先出
SDWebImageDownloaderLIFOExecutionOrder
};
属性初始化
-
shouldDecompressImages
, 是否解码图片,默认YES。 -
executionOrder
, 队列顺序,默认先进先出。 -
maxConcurrentOperationCount
,队列最大任务并发数,默认6个。 -
downloadTimeout
,任务超时时间,默认15秒。
另外,会创建一个NSURLSession
用来进行请求,注意,不是使用sharedSession
,而是单独创建一个session。在Downloader对象销毁时会调用session的- (void)invalidateAndCancel
方法并置空。
另外还会会维护一个URLOperations
字段来存储所有的operation,这个字典的操作会放在一个自营第队列中,来保证线程安全。
下载图片
- (nullable SDWebImageDownloadToken *)downloadImageWithURL:(nullable NSURL *)url
options:(SDWebImageDownloaderOptions)options
progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
completed:(nullable SDWebImageDownloaderCompletedBlock)completedBlock
使用URL创建请求request,根据options参数设置request的缓存策略(是否使用NSURLCache
缓存)、设置cookie处理方式、设置请求头等。
如果URL对应的SDWebImageDownloaderOperation
不存在于内部字典中,则会以request创建一个op,根据options参数设置op的优先级,并添加到队列中。如果队列的执行顺序是后进先出,则将之前的最后一个op添加依赖到新的op上来确保执行顺序。
方法会返回一个Token
,可以用来取消对应的op。
SDWebImageCombinedOperation
@interface SDWebImageCombinedOperation : NSObject <SDWebImageOperation>
@property (assign, nonatomic, getter = isCancelled) BOOL cancelled;
@property (copy, nonatomic, nullable) SDWebImageNoParamsBlock cancelBlock;
@property (strong, nonatomic, nullable) NSOperation *cacheOperation;
@end
@implementation SDWebImageCombinedOperation
- (void)setCancelBlock:(nullable SDWebImageNoParamsBlock)cancelBlock {
// check if the operation is already cancelled, then we just call the cancelBlock
if (self.isCancelled) {
if (cancelBlock) {
cancelBlock();
}
_cancelBlock = nil; // don't forget to nil the cancelBlock, otherwise we will get crashes
} else {
_cancelBlock = [cancelBlock copy];
}
}
- (void)cancel {
self.cancelled = YES;
if (self.cacheOperation) {
[self.cacheOperation cancel];
self.cacheOperation = nil;
}
if (self.cancelBlock) {
self.cancelBlock();
// TODO: this is a temporary fix to #809.
// Until we can figure the exact cause of the crash, going with the ivar instead of the setter
// self.cancelBlock = nil;
_cancelBlock = nil;
}
}
这个类遵守SDWebImageOperation
协议,实现了- (void)cancel
方法,并且有一个NSOperation
类型的cacheOperation
属性和一个cancelBlock
属性。
在- (void)cancel
方法中,cacheOperation
的取消方法会被调用,然后调用cencelBlock
。
SDWebImageManager
SD中的核心类,由它来协调SDImageCache
和SDWebImageDownloader
协同工作。
下载选项:
typedef NS_OPTIONS(NSUInteger, SDWebImageOptions) {
// 下载失败过的URL会从黑名单中删除并重新尝试下载
SDWebImageRetryFailed = 1 << 0,
// 对应SDWebImageDownloaderLowPriority,低优先级的下载任务不会在UI交互时启动
SDWebImageLowPriority = 1 << 1,
// 只进行内存缓存
SDWebImageCacheMemoryOnly = 1 << 2,
// 对应SDWebImageDownloaderProgressiveDownload,渐进式下载显示
SDWebImageProgressiveDownload = 1 << 3,
/**
* 对应SDWebImageDownloaderUseNSURLCache
* 即使图片有SD缓存,也会遵循HTTP的缓存控制,在需要的时候从远端获取图片,比如远端图片有新版本。本地硬盘缓存会由NSURLCache进行处理,而不是SDWebImage。
* 这个在同一个URL对应的图片可能发生改变时很有用,比如一个头像图片的URL。
* 如果缓存图片被刷新了,回调会先被调用一次,参数是缓存图片,然后用请求的图片作为参数再调用一次。
*/
SDWebImageRefreshCached = 1 << 4,
// 对应SDWebImageDownloaderContinueInBackground,App进入后台时继续下载图片,如果后台任务超时,任务会被取消。
SDWebImageContinueInBackground = 1 << 5,
/**
* 对应SDWebImageDownloaderHandleCookies,会设置
* NSMutableURLRequest.HTTPShouldHandleCookies = YES;
* 来处理存储在NSHTTPCookieStore中的cookie
*/
SDWebImageHandleCookies = 1 << 6, 会设置
// 对应SDWebImageDownloaderAllowInvalidSSLCertificates,允许不受信任的SSL证书
SDWebImageAllowInvalidSSLCertificates = 1 << 7,
// 对应SDWebImageDownloaderHighPriority,高优先级任务会在下载队列中被优先下载
SDWebImageHighPriority = 1 << 8,
// 延迟占位图展示,默认占位图会在下载图片前显示,使用此项后,占位图会在下载任务完成后没有图片的情况下才显示
SDWebImageDelayPlaceholder = 1 << 9,
/** 默认SDWebImageManager是不会调用下面的代理方法预处理下载好的动图的:
* imageManager:transformDownloadedImage:withURL:
* 使用这个选项,manager在下载完成后、缓存图片和回调前会调用此代理方法获取处理过的图片
*/
SDWebImageTransformAnimatedImage = 1 << 10,
// 默认会自动将图片设置到视图上,使用此项取消自动设置
SDWebImageAvoidAutoSetImage = 1 << 11,
// 对应SDWebImageDownloaderScaleDownLargeImages,如果设置了`SDWebImageProgressiveDownload`,则此项会失效
SDWebImageScaleDownLargeImages = 1 << 12
};
协议方法
@class SDWebImageManager;
@protocol SDWebImageManagerDelegate <NSObject>
@optional
// 控制图片是否应该下载
- (BOOL)imageManager:(nonnull SDWebImageManager *)imageManager shouldDownloadImageForURL:(nullable NSURL *)imageURL;
// 在图片下载后、缓存图片前调用此方法处理图片,方法会在全局队列中调用
- (nullable UIImage *)imageManager:(nonnull SDWebImageManager *)imageManager transformDownloadedImage:(nullable UIImage *)image withURL:(nullable NSURL *)imageURL;
@end
- (nullable NSString *)cacheKeyForURL:(nullable NSURL *)url;
@property (nonatomic, copy, nullable) SDWebImageCacheKeyFilterBlock cacheKeyFilter;
cacheKeyForURL:
返回URL对应的缓存key,如果有cacheKeyFilter
会使用它的返回值作为最终的值,否则返回url.absoluteString
。
- (nullable id <SDWebImageOperation>)loadImageWithURL:(nullable NSURL *)url
options:(SDWebImageOptions)options
progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
completed:(nullable SDInternalCompletionBlock)completedBlock;
如果URL对应的图片不在缓存中,则进行下载。
方法会创建并返回一个SDWebImageCombinedOperation
类型的operation,operation的cacheOperation
是由[self.imageCache queryCacheOperationForKey:...]
方法返回的一个NSoperation
。
如果需要下载图片,则会调用[self.imageDownloader downloadImageWithURL:...]
方法下载图片,方法返回一个SDWebImageDownloadToken
类型的token,这个token会被前面的operation的cancelBlock
捕获,在block中使用这个token来调用[self.imageDownloader cancel:token]
方法进行取消。
- (void)saveImageToCache:(nullable UIImage *)image forURL:(nullable NSURL *)url
将图片缓存到内存和硬盘。
- (void)cachedImageExistsForURL:(nullable NSURL *)url
completion:(nullable SDWebImageCheckCacheCompletionBlock)completionBlock;
异步确认url对应的图片是否存在于缓存中,先从内存查找,否则异步在硬盘查找,回调在主线程执行。
- (void)diskImageExistsForURL:(nullable NSURL *)url
completion:(nullable SDWebImageCheckCacheCompletionBlock)completionBlock;
异步确认URL对应的图片是否存在于硬盘缓存, 不会加载图片,回调在主线程执行。
各个Operation
之间的关系
在上面的各个类中,可以看到有好几种Operation
,和取消方法。总结如下:
NSOperation
1. - (void)cencel;
SDWebImageOperation协议
1. - (void)cancel;
SDImageCache:
`queryCacheOperationForKey:...`方法
1. query操作首先查找内存, 命中则回调,返回nil
2. 返回NSOperation,异步查找硬盘,回调。
SDWebImageDownloaderOperation:
1. 内部维护callbackBlocks数组,数组元素为Dic<KEY,BLOCK>
2. `- (void)cancel`,调用父类NSOperation的cancel方法,调用dataTask的cancel方法,设置状态
3. `- (BOOL)cancel:(nullable id)token`,从内部维护的callbackBlocks数组中删除对应的回调,调用`- (void)cancel`方法
SDWebImageDownloader:
1. 内部维护URLOperations字典,key为URL,值为SDWebImageDownloaderOperation
2. `- (void)cancel:(nullable SDWebImageDownloadToken *)token`,
使用token.URL从URLOperations字典中获取downloaderOp, 调用downloaderOp的
`- (BOOL)cancel:(nullable id)token`方法。将downloaderOp从字典中删除。
3. `downloadImageWithURL:..`方法返回SDWebImageDownloadToken,
方法内会创建SDWebImageDownloaderOperation,将downloaderOp添加到URLOperations字典中,将downloaderOp添加到队列中。
4. `- (void)cancelAllDownloads`,方法会调用`[self.downloadQueue cancelAllOperations]`,系统会对队列内的所有operation调用`- (void)cancel`方法。
SDWebImageCombinedOperation:
1. 遵守SDWebImageOperation协议
2. 有一个NSOperation类型cacheOperation属性
3. 有一个cancelBlock属性
4. `- (void)cancel`方法内会调用cancelBlock和cacheOperation的cancel方法
SDWebImageManager
1. 内部维护一个runningOperations数组,里面的对象是SDWebImageCombinedOperation
2. `loadImageWithURL:..`,会创建`SDWebImageCombinedOperation`类型的combinedOp,
创建combinedOp.cacheOperation为`[SDImageCache.sharedCache queryCacheOperationForKey:...]`的返回值,
如果没有查找到缓存图片,则下载。下载会调用`[SDWebImageDownloader.sharedDownloader downloadImageWithURL:...]`方法,
返回的SDWebImageDownloadToken被combinedOp的cancelBlock捕获。
3. 在queryCache或者downloadImage的过程中,如果combinedOp被cancel,则会被从runningOperations中删除。