SDWebImage源码阅读笔记
SDWebImageOptions
定义基本的配置选项,简单翻译了一下注释
- SDWebImageRetryFailed 默认,当从一个URL下载失败,这个URL会被加入黑名单,之后不会重试。不过黑名单是存在内存中的,所以当程序下次运行时还是有机会重新尝试下载这个图片的。
- SDWebImageLowPriority 默认,图片会在UI交互进行交互的时候下载,这个选项可以禁用这个特性,将下载延迟到UIScrollView停止滚动之后
- SDWebImageCacheMemoryOnly 禁用磁盘缓存
- SDWebImageProgressiveDownload 变下载边显示,默认是下载完成后才显示
- SDWebImageRefreshCached即使图片被缓存,也会遵守HTTP响应缓存控制,在远端刷新时重新下载。SDWebImage磁盘缓存管理将被NSURLCache代替,这将导致轻微的性能下降。这个选项有助于处理同一个URL下图片不同的情况。 如果缓存图片被刷新,完成的block回调被已缓存的图片和最终的图片各调用一次。
- SDWebImageContinueInBackground 在iOS4以上系统,启用后台下载
- SDWebImageHandleCookies cookie管理
- SDWebImageAllowInvalidSSLCertificates 允许不受信任的SSL证书,用于测试。
- SDWebImageHighPriority 默认的,图片按顺序加载,这个选项将图片移动到队列最前面
- SDWebImageDelayPlaceholder 默认的,占位图在图片加载时加载,这个选项会延迟占位图的加载到图片完成加载之后
- SDWebImageTransformAnimatedImage 由于大多数转化代码可能会损坏图片动画,通常是不对其调用transformDownloadedImage 代理方法,使用这个枚举来启用
- SDWebImageAvoidAutoSetImage 默认情况,图片会在下载后添加到imageView,这个选项允许你手动管理图片当图片下载好之后。
接下来是代理SDWebImageManagerDelegate的定义:
-(BOOL)imageManager:(SDWebImageManager *)imageManager shouldDownloadImageForURL:(NSURL *)imageURL;
当这个图片不在缓存中,将要被下载时调用
-(UIImage *)imageManager:(SDWebImageManager *)imageManager transformDownloadedImage:(UIImage *)imagewithURL:(NSURL *)imageURL;
允许图片在被下载完成后缓存到磁盘和内存之前立即被转换(转换transform应该就是将二进制数据格式化吧我猜)。这个方法在子线程执行以防止阻碍主线程。
接下来是一段简单的使用说明:
SDWebImageManager是其他category的前置类,即其他category都是使用SDWebImageManager进行下载图片的。SDWebImageManager将下载器(SDWebImageDownloader)和缓存(SDImageCache)链接起来。你可以使用这个类直接下载和缓存图片,而不需要使用UIView,例如
[managerdownloadImageWithURL:imageURL options:0 progress:nil completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOLfinished, NSURL *imageURL) {
if(image) {
// do something with image
}
}];
然后是SDWebImageManager的属性和方法的定义。值得说明的是:
@property (nonatomic, copy) SDWebImageCacheKeyFilterBlock cacheKeyFilter;
缓存过滤器是一个block,SDWebImageManager需要将一个URL转换为缓存的key时使用它。它可以移除URL的动态部分,下面是例子:
[[SDWebImageManagersharedManager] setCacheKeyFilter:^(NSURL *url) {
url = [[NSURL alloc] initWithScheme:url.scheme host:url.host path:url.path];
return [url absoluteString];
}];
其他的方法基本上都是见名知义的,就不需要过多解释了。来到.m文件,三百多行代码中有足足180行是
- (id <SDWebImageOperation>)downloadImageWithURL:options:progress:completed:
方法的实现,所以只要看看这个方法即可。
首先是一个断言(我自己几乎从来没用过断言,以后应该改进啊),当completed block为空的时候会触发断言。然后作者非常周全的考虑到了很多人会犯的一个错误是将string直接作为URL参数传入,XCode不会抛出警告,所以作者在这里判断并将字符串作为参数初始化一个NSURL对象并赋值给url。然后又判断url参数是否为NSURL,以防止被传入一个错误的类型。
接下来,作者使用线程锁检查url是否在黑名单中,并判断url的长度是否为0,最后再对参数做一次检查😅。然后将当前的操作添加到一个集合中去,用于取消下载等操作。
终于到正戏了,首先检查磁盘缓存,细节我就不每一行都解释了,包括是否操作被取消了,即时调用代理方法,根据配置做不同的操作等等,基本都是逻辑判断了。最后返回操作对象。
迫不及待要看看平时最常用的UIImageView+WebCache
这个分类了,看看头文件,方法不多,而且功能都很具体,直接看最常用的- (void)sd_setImageWithURL:(NSURL*)url;
方法吧。
嗯,只有一句,[self sd_setImageWithURL:url placeholderImage:nil options:0 progress:nil completed:nil];
这个在意料之中的,平时自己封装工具也经常这么干。点进去接着看。
这下进入正菜了。首先取消当前的加载任务,因为是ImageView 的分类,一个ImageView自然只需要加载一个image,很好,继续。runtime闪亮登场!objc_setAssociatedObject(self, &imageURLKey, url, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
这个也是runtime里很常用的一个函数,将一个属性绑定给一个对象。这里将url绑定给了imageView,这样imageView就有了url这个属性。接下来处理了一下占位图和ActivityIndicatorView。然后果然看到了SDWebImageManager的- (id <SDWebImageOperation>)downloadImageWithURL:options:progress:completed:
方法。然后就是在完成的回调里面各种判断和执行完成回调了。
可以看到,SDWebImage的设计方式很合理,首先定义基本的管理类来管理这个工具的核心功能——对于SDWebImage就是下载(SDWebImageDownloader)和缓存(SDImageCache)。具体的功能分别在对应的类中实现。最后通过分类的形式创建出具体方便的使用方式和场景。一般的工具类都可以借鉴这种设计方式。
最后附带一个SDWebImageCompat
类的简介,对于想要公开的代码库,兼容性是很重要的问题,而这些内容在一般的具体业务中很少被考虑到。
#ifdef __OBJC_GC__
#errorSDWebImage does not support Objective-C Garbage Collection
#endif
//不支持OC的垃圾回收机制
#if IPHONE_OS_VERSION_MIN_REQUIRED != 20000 &&IPHONE_OS_VERSION_MIN_REQUIRED < _IPHONE5_0
#errorSDWebImage doesn't support Deployment Target version < 5.0
#endif
//不支持iOS5以前的版本
#if!TARGET_OS_IPHONE
#import <AppKit/AppKit.h>
#ifndefUIImage
#defineUIImage NSImage
#endif
#ifndefUIImageView
#defineUIImageView NSImageView
#endif
#else
//在Mac下将UIKit控件定义为AppKit控件,用于支持macOS
#ifndefNS_ENUM
#defineNS_ENUM(_type, _name) enum _name : _type _name; enum _name : _type
#endif
#ifndefNS_OPTIONS
#defineNS_OPTIONS(_type, _name) enum _name : _type _name; enum _name : _type
#endif
//定义 NS_ENUM 和 NS_OPTIONS(用于支持较早的OC版本?)
#ifOS_OBJECT_USE_OBJC
#undef SDDispatchQueueRelease
#undef SDDispatchQueueSetterSementics
#define SDDispatchQueueRelease(q)
#define SDDispatchQueueSetterSementics strong
#else
#undefSDDispatchQueueRelease
#undefSDDispatchQueueSetterSementics
#defineSDDispatchQueueRelease(q) (dispatch_release(q))
#defineSDDispatchQueueSetterSementics assign
#endif
//定义了两个类型,但是不明白为什么要这么定义😅
#if!__has_feature(objc_arc)
#errorSDWebImage is ARC only. Either turn on ARC for the project or use -fobjc-arcflag
#endif
//声明SDWebImage 只支持ARC
NSString *constSDWebImageErrorDomain = @"SDWebImageErrorDomain";
//最后定义了SDWebImage 的错误域,用于输出错误信息。