SDWebImage介绍
提供 UIImageView, UIButton, MKAnnotationView 的分类,用来加载网络图片,并进行缓存管理;
异步方式来下载网络图片
异步方式: memory (内存)+ disk (磁盘) 来缓存网络图片,自动管理缓存;
后台图片解码,转换及压缩;
- 空间换时间https://www.cocoanetics.com/2011/10/avoiding-image-decompression-sickness/
同一个 URL 不会重复下载;
失效的 URL 不会被无限重试;
支持 GIF动画 及 WebP 格式;
开启 子线程 进行耗时操作,不阻塞主线程;
下载完图片的储存发生在SDWebImageManager里面(下载operation也是这里面创建的),可以作为提前缓存图片的方式
SDWebImageContext
SDWebImageContextSetImageOperationKey
- 简单来说,这个key对应的value用于指定当前的id
保存在NSMapTable中的key,后续的cancel、remove都需要通过这个key来找到对应的线程。 - 当指定同一个key加载图片时会先cancel之前存在的线程。
- SDWebImageContextSetImageOperationKey并不是在所有地方使用都生效的。
比如UIButton的sd_setImageWithURL:和sd_setBackgroundImageWithURL:系列方法。
在其内部需要通过这个这值来保存、区分不同状态(UIControlState)的图片加载线程,所以即使设置了SDWebImageContextSetImageOperationKey也会被覆盖:
SDWebImageContextCustomManager
可以传入一个自定义的SDWebImageManager,默认使用[SDWebImageManager sharedManager]
SDWebImageContextImageTransformer
可以传入一个id
- 在SDWebImageManager中也可以设置一个id
默认为nil,但是只有SDWebImageContext没有配置SDWebImageContextImageTransformer,才会使用它。
也就是配置优先级 SDWebImageContext>SDWebImageManager - 如果设置了id
不会缓存原始图片,只缓存处理后的图片。 - 对于同个图片、不同参数的id
会被认为是不同的图片:会产生不同的缓存文件、会重复下载。
SDWebImageContextImageScaleFactor
在NSData -> UIImage时对图片放大比例,是个大于1的CGFloat值,默认值:
SDWebImageContextStoreCacheType
定义图片缓存规则具体看 SDImageCacheType中的定义。
SDWebImageContextDownloadRequestModifier
可以传入一个id
- SDWebImageContextDownloadRequestModifier协议比较简单,只需要实现一个方法,返回一个修改后的NSURLRequest即可:
- 内建了一个SDWebImageDownloaderRequestModifier对象,可以使用Block方便的修改NSURLRequest
SDWebImageContextCacheKeyFilter
可以传入一个id
- SDWebImageCacheKeyFilter协议也比较简单,只需要实现一个方法,返回一个对应的缓存key字符串即可
- (nullable NSString *)cacheKeyForURL:(nonnull NSURL *)url;
- 内建了一个SDWebImageCacheKeyFilter对象,可以使用Block方便的返回缓存key
SDWebImageContextCacheSerializer
- 可以传入一个id
,转换需要缓存的图片格式。 - 在SDWebImageManager中也可以设置一个id
默认为nil,但是只有SDWebImageContext没有配置SDWebImageContextCacheSerializer,才会使用它。
也就是配置优先级 SDWebImageContext>SDWebImageManager - 通常用于需要缓存的图片格式与下载的图片格式不相符的时候,如:下载的时候为了节约流量、减少下载时间使用了WebP格式,但是如果缓存也用WebP,每次从缓存中取图片都需要经过一次解压缩,这样是比较影响性能的,就可以使用id
,实现其中的协议方法:
SDWebImageContextLoaderCachedImage
可以传入一个UIImage的缓存图像。
- 这个值比较特殊,它是定义在SDImageLoader.m中的。
- 这个值可以认为是SDWebImage是内部用来从SDWebImageManager向SDWebImageDownloader(id
)传递缓存图像的,自定义实现SDImageLoader协议可能会用到这个值,其他情况一般不会用到。 - 这个属性只有在 SDWebImageOptions包含SDWebImageRefreshCached策略时才生效,也就是他是SDWebImageRefreshCached这个策略的配套值。
- SDWebImageRefreshCached这个策略用于那些图片URL是静态的(图片更新时URL是不变的,SD给的例子是 Facebook graph api profile pics),这个时候它会根据HTTP header的 cache-control 字段来控制缓存并且使用NSURLCache来缓存图片,SDWebImageDownloader(id
)中判断SDWebImageContextLoaderCachedImage存在并且策略是SDWebImageRefreshCached的情况,仍然会发起请求。
SDWebImageDownloader
- self.URLOperations
包含所有的下载队列
这句话很重要 url和callback关联dic
// The URL will be used as the key to the callbacks dictionary so it cannot be nil. If it is nil immediately call the completed block with no image or data.
downloadOperationCancelToken 返回绑定当前operation的callback、progressBlock, 是一个可变字典
operation.loaderOperation = SDWebImageDownloadToken
- addHandlersForProgress
其实是向 SDWebImageDownloaderOperation 里面的callbackBlocks注册回调callback
SDWebImageDownloadToken 其实包含
- url
- request
- downloadOperationCancelToken (这是一个dic,又包含)
- callback
- progressBlock
cancel的时候
其中一个队列SDWebImageDownloaderOperation,包含所有url对应的回调
- self.callbackBlocks
options参数:
SDWebImageOptions属性 | 说明 |
---|---|
SDWebImageRetryFailed | 默认情况下,如果一个url在下载的时候失败了,那么这个url会被加入黑名单并且library不会尝试再次下载,这个flag会阻止library把失败的url加入黑名单 |
SDWebImageLowPriorit | 默认情况下,图片会在交互发生的时候下载(例如你滑动tableview的时候),这个flag会禁止这个特性,导致的结果就是在scrollview减速的时候,才会开始下载 |
SDWebImageProgressiveLoad | 这个flag启动渐进式下载图像,类似浏览器加载图像那样逐步显示,默认情况下,图像仅是在下载完成后显示 |
SDWebImageRefreshCached | 一个图片缓存了,还是会重新请求.并且缓存侧略依据NSURLCache而不是SDWebImage。即在URL没变但是服务器图片发生更新时使用 |
SDWebImageContinueInBackground | 启动后台下载,实现原理是通过向系统询问后台的额外时间来完成请求的。 如果后台任务到期,则操作将被取消。 |
SDWebImageHandleCookies | 当设置了NSMutableURLRequest.HTTPShouldHandleCookies = YES 时,可以控制存储NSHTTPCookieStorage中的cookie |
SDWebImageAllowInvalidSSLCertificates | 允许不安全的SSL证书,用于测试环境,在正式环境中谨慎使用 |
SDWebImageHighPriority | 默认情况下,image在装载的时候是按照他们在队列中的顺序装载的(就是先进先出)。这个flag会把他们移动到队列的前端,并且立刻装载,而不是等到当前队列装载的时候再装载。 |
SDWebImageDelayPlaceholder | 默认情况下,占位图会在图片下载的时候显示.这个flag开启会延迟占位图显示的时间,等到图片下载完成之后才会显示占位图. |
SDWebImageTransformAnimatedImage | 通常不会在可动画的图像上调用 transformDownloadedImage 代理方法,因为大多数转换代码会破坏动画文件,这个flag为尝试转换 |
SDWebImageAvoidAutoSetImage | 图片在下载后被加载到imageView。但是在一些情况下,我们想要设置一下图片(引用一个滤镜或者加入透入动画)这个flag来手动的设置图片在下载图片成功后 |
SDWebImageScaleDownLargeImages | 默认情况下,图像将根据其原始大小进行解码。 在iOS上,此flat会将图片缩小到与设备的受限内存兼容的大小。 但如果设置了SDWebImageAvoidDecodeImage 则此flat不起作用。 如果设置了SDWebImageProgressiveLoad 它将被忽略。 |
SDWebImageQueryMemoryData | 默认情况下,当图像已缓存在内存中时,我们不会查询图像数据。 此flat则强制查询图像数据。 此查询是异步的,除非指定SDWebImageQueryMemoryDataSync |
SDWebImageQueryMemoryDataSync | 结合SDWebImageQueryMemoryData 设置同步查询图像数据(一般不建议这么使用,除非是在同一个runloop 里避免单元格复用时发生闪现) |
SDWebImageQueryDiskDataSync | 如果内存查询没有的时候,强制同步磁盘查询(这三个查询可以组合使用,一般不建议这么使用,除非是在同一个runloop 里避免单元格复用时发生闪现) |
SDWebImageFromCacheOnly | 默认情况下,当缓存丢失时,SD将从网络下载图像。 此flat可以防止这样,使其仅从缓存加载。 |
SDWebImageFromLoaderOnly | 默认情况下,SD在下载之前先从缓存中查找,此flat可以防止这样,使其仅从网络下载 |
SDWebImageForceTransition | 默认情况下,SD在图像加载完成后使用SDWebImageTransition 进行某些视图转换,此转换仅适用于从网络下载图像。 此flat可以强制为内存和磁盘缓存应用视图转换。 |
SDWebImageAvoidDecodeImage | 默认情况下,SD在查询缓存和从网络下载时会在后台解码图像,这有助于提高性能,因为在屏幕上渲染图像时,需要首先对其进行解码。这发生在Core Animation 的主队列中。然而此过程也可能会增加内存使用量。 如果由于过多的内存消耗而遇到问题,可以用此flat禁止解码图像。 |
SDWebImageDecodeFirstFrameOnly | 默认情况下,SD会解码动画图像,该flat强制只解码第一帧并生成静态图。 |
SDWebImagePreloadAllFrames | 默认情况下,对于SDAnimatedImage ,SD会在渲染过程中解码动画图像帧以减少内存使用量。 但是用户可以指定将所有帧预加载到内存中,以便在大量imageView共享动画图像时降低CPU使用率。这实际上会在后台队列中触发preloadAllAnimatedImageFrames (仅限磁盘缓存和下载)。 |
SDImageTransformer的类型
SDImageCache
NSCache
- 自动删除机制:当系统内存紧张时,NSCache会自动删除一些缓存对象
- 线程安全:从不同线程中对同一个 NSCache 对象进行增删改查时,不需要加锁
- 不同于 NSMutableDictionary,NSCache存储对象时不会对 key 进行 copy 操作
小Tip
- 运行时存取关联对象:
- 数组的写操作需要加锁(多线程访问,避免覆写)
1
2
3
4
5
6
7
8
9
10
11
12
13
14//给self.runningOperations加锁
//self.runningOperations数组的添加操作
@synchronized (self.runningOperations) {
[self.runningOperations addObject:operation];
}
//self.runningOperations数组的删除操作
- (void)safelyRemoveOperationFromRunning:(nullable SDWebImageCombinedOperation*)operation {
@synchronized (self.runningOperations) {
if (operation) {
[self.runningOperations removeObject:operation];
}
}
} - 确保在主线程的宏:
1
2
3
4
5
6
7
8
9
10
11
12
13dispatch_main_async_safe(^{
//将下面这段代码放在主线程中
[self sd_setImage:placeholder imageData:nil basedOnClassOrViaCustomSetImageBlock:setImageBlock];
});
//宏定义:
#define dispatch_main_async_safe(block)\
if (strcmp(dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL), dispatch_queue_get_label(dispatch_get_main_queue())) == 0) {\
block();\
} else {\
dispatch_async(dispatch_get_main_queue(), block);\
}
#endif - 设置不能为nil的参数
1
2
3
4
5
6
7
8
9- (nonnull instancetype)initWithCache:(nonnull SDImageCache *)cache downloader:(nonnull SDWebImageDownloader *)downloader {
if ((self = [super init])) {
_imageCache = cache;
_imageDownloader = downloader;
_failedURLs = [NSMutableSet new];
_runningOperations = [NSMutableArray new];
}
return self;
} - 容错,强制转换类型
1
2
3if ([url isKindOfClass:NSString.class]) {
url = [NSURL URLWithString:(NSString *)url];
}
UIImageView分类方法,分流到了UIView的分类方法,注释如下
1 | /** |