iOS -SDWebImage的使用和底层原理
一、SDWebImage的使用
1、SDWebImage的安装集成有2种方式:
(1)直接到github地址下载,链接https://github.com/rs/SDWebImage
(2)用cocoapods安装,在文件夹生成的podfile文件中添加pod 'SDWebImage' ,终端cd + 文件位置,然后pod install即可
2、UITableView中导入头文件UIImageView+WebCache.h
[cell.imageView sd_setImageWithURL:[NSURL URLWithString:@"http://www.domain.com/path/to/image.jpg"] placeholderImage:[UIImage imageNamed:@"placeholder.png"]];
如果在加载完图片后,需要做些其他操作,可以使用block回调
[cell.imageView sd_setImageWithURL:[NSURL URLWithString:@"http://www.domain.com/path/to/image.jpg"]
placeholderImage:[UIImage imageNamed:@"placeholder.png"]
completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) {
... completion code here ...
}];
3、SDWebImageManager的使用
UIImageView(WebCache) 分类的核心在于 SDWebImageManager 的下载和缓存处理,SDWebImageManager将图片下载和图片缓存组合起来了。SDWebImageManager也可以单独使用。
SDWebImageManager *manager = [SDWebImageManager sharedManager];
[manager loadImageWithURL:imageURL
options:0
progress:^(NSInteger receivedSize, NSInteger expectedSize) {
// progression tracking code
}
completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
if (image) {
// do something with image
}
}];
4、单独使用SDWebImageDownloader异步下载图片
我们还可以单独使用 SDWebImageDownloader 来下载图片,但是图片内容不会缓存。
SDWebImageDownloader *downloader = [SDWebImageDownloader sharedDownloader];
[downloader downloadImageWithURL:imageURL
options:0
progress:^(NSInteger receivedSize, NSInteger expectedSize) {
// progression tracking code
}
completed:^(UIImage *image, NSData *data, NSError *error, BOOL finished) {
if (image && finished) {
// do something with image
}
}];
5、单独使用SDImageCache异步缓存图片
SDImageCache 支持内存缓存和异步的磁盘缓存(可选),如果你想单独使用 SDImageCache 来缓存数据的话,可以使用单例,也可以创建一个有独立命名空间的 SDImageCache 实例。
添加缓存的方法:
[[SDImageCache sharedImageCache] storeImage:myImage forKey:myCacheKey];
默认情况下,图片数据会同时缓存到内存和磁盘中,如果你想只要内存缓存的话,可以使用下面的方法:
[[SDImageCache sharedImageCache] storeImage:myImage forKey:myCacheKey toDisk:NO];
读取缓存时可以使用 queryDiskCacheForKey:done: 方法,图片缓存的 key 是唯一的,通常就是图片的 absolute URL。
SDImageCache *imageCache = [[SDImageCache alloc] initWithNamespace:@"myNamespace"];
[imageCache queryDiskCacheForKey:myCacheKey done:^(UIImage *image) {
// image is not nil if image was found
}];
6、自定义缓存key
有时候,一张图片的 URL 中的一部分可能是动态变化的(比如获取权限上的限制),所以我们只需要把 URL 中不变的部分作为缓存用的 key。
SDWebImageManager.sharedManager.cacheKeyFilter = ^(NSURL *url) {
url = [[NSURL alloc] initWithScheme:url.scheme host:url.host path:url.path];
return [url absoluteString];
};
二、使用过程中常见问题
问题 1:使用 UITableViewCell 中的 imageView 加载不同尺寸的网络图片时会出现尺寸缩放问题。
解决方案:
自定义 UITableViewCell,重写 -layoutSubviews 方法,调整位置尺寸;
或者直接弃用 UITableViewCell 的 imageView,自己添加一个 imageView 作为子控件。
问题 2:图片刷新问题:SDWebImage 在进行缓存时忽略了所有服务器返回的 caching control 设置,并且在缓存时没有做时间限制,这也就意味着图片 URL 必须是静态的了,要求服务器上一个 URL 对应的图片内容不允许更新。但是如果存储图片的服务器不由自己控制,也就是说 图片内容更新了,URL 却没有更新,这种情况怎么办?
解决方案:在调用 sd_setImageWithURL: placeholderImage: options:方法时设置 options 参数为 SDWebImageRefreshCached,这样虽然会降低性能,但是下载图片时会照顾到服务器返回的 caching control。
问题 3:在加载图片时,如何添加默认的 progress indicator ?
解决方案:在调用 -sd_setImageWithURL:方法之前,先调用下面的方法:
[imageView sd_setShowActivityIndicatorView:YES];
[imageView sd_setIndicatorStyle:UIActivityIndicatorViewStyleGray];
问题4:如果在加载图片的过程中出现程序报错(App Transport Security has blocked a cleartext HTTP (http://) resource load since it is insecure. Temporary exceptions can be configured via your app's Info.plist file.)
你需要操作如下--------
(1)、在Info.plist中添加 NSAppTransportSecurity 类型 Dictionary ;
(2)、在 NSAppTransportSecurity 下添加 NSAllowsArbitraryLoads 类型Boolean ,值设为 YES;
三、SDWebImage底层原理
1)当我门需要获取网络图片的时候,我们首先需要的便是URL,获得URL后我们SDWebImage实现的并不是直接去请求网路,而是检查图片缓存中有没有和URl相关的图片,如果有则直接返回image,如果没有则进行下一步。
2)当图片缓存中没有图片时,SDWebImage依旧不会直从网络上获取,而是检查沙盒中是否存在图片,如果存在,则把沙盒中对应的图片存进image缓存中,然后按着第一步的判断进行。
3)如果沙盒中也不存在,则显示占位图,然后根据图片的下载队列缓存判断是否正在下载,如果下载则等待,避免二次下载。如果不存则创建下载队列,下载完毕后将下载操作从队列中清除,并且将image存入图片缓存中。
4)刷新UI(当然根据实际情况操作)将image存入沙盒缓存。
四、SDWebImage源码实现步骤
常见的四种加载方式
1、无占位图直接加载(如果缓存中存在改图片则直接获取无需重新下载增加磁盘缓存)
- (void)sd_setImageWithURL:(nullable NSURL *)url {
[self sd_setImageWithURL:url placeholderImage:nil options:0 progress:nil completed:nil];
}
2、有占位图直接加载(如果URL加载不到则展示占位图,如果缓存中存在改图片则直接获取无需重新下载增加磁盘缓存)
- (void)sd_setImageWithURL:(nullable NSURL *)url placeholderImage:(nullable UIImage *)placeholder {
[self sd_setImageWithURL:url placeholderImage:placeholder options:0 progress:nil completed:nil];
}
3、有占位图直接加载,并且实现图片加载完之后的Block可以继续完成下一步操作(如果URL加载不到则展示占位图,如果缓存中存在改图片则直接获取无需重新下载增加磁盘缓存)
- (void)sd_setImageWithURL:(nullable NSURL *)url placeholderImage:(nullable UIImage *)placeholder completed:(nullable SDExternalCompletionBlock)completedBlock {
[self sd_setImageWithURL:url placeholderImage:placeholder options:0 progress:nil completed:completedBlock];
}
4、可以选择options的形式加载图片,(如果URL加载不到则展示占位图,如果缓存中存在改图片则直接获取无需重新下载增加磁盘缓存)
- (void)sd_setImageWithURL:(nullable NSURL *)url placeholderImage:(nullable UIImage *)placeholder options:(SDWebImageOptions)options {
[self sd_setImageWithURL:url placeholderImage:placeholder options:options progress:nil completed:nil];
}
/*使用可更换optionsType的加载方式
-------------Options 枚举下的加载方式-----------
SDWebImageRetryFailed 默认情况下,当URL无法下载时,URL就会被列入黑名单,这样库就不会继续尝试了。此标记禁用此黑名单。
SDWebImageLowPriority 默认情况下,图像下载是在UI交互过程中启动的,这标志禁用该特性,导致在UIScrollView减速方面延迟下载。
SDWebImageCacheMemoryOnly 此标记禁用磁盘缓存
SDWebImageProgressiveDownload 此标志可以进行渐进式下载,在下载过程中,图像会逐步显示,就像浏览器所做的那样。默认情况下,图像只显示一次完全下载。
SDWebImageRefreshCached 即使缓存了映像,也要尊重HTTP响应缓存控制,并在需要的情况下从远程位置刷新映像。磁盘缓存将由NSURLCache来处理,而不是使用SDWebImage,这会导致轻微的性能下降。这个选项有助于处理在同一个请求URL后面更改的图像,例如Facebook图形api概要图。如果刷新了缓存的图像,那么完成块就会被缓存的图像和最后的图像再次调用一次。只有当你不能用嵌入的缓存破坏参数使你的url静态时,才使用这个标志。
SDWebImageContinueInBackground 在iOS 4+中,如果应用程序进入后台,可以继续下载图片。这是通过请求系统在后台获得额外的时间来完成请求完成的。如果后台任务过期,操作将被取消。
SDWebImageHandleCookies 通过设置NSMutableURLRequest来处理存储在NSHTTPCookieStore中的cookie。HTTPShouldHandleCookies =是的;
SDWebImageAllowInvalidSSLCertificates 启用不受信任的SSL证书。用于测试目的。在生产中使用谨慎。
SDWebImageHighPriority 默认情况下,图像按顺序装载在队列中。这个标志把它们移到队列的前面。
SDWebImageDelayPlaceholder 默认情况下,在图像加载时加载占位符图像。此标志将延迟加载占位符图像,直到图像完成加载。
SDWebImageTransformAnimatedImage 我们通常不会在动画图像上调用transformdownloade昏暗委托方法,因为大多数转换代码会把它搞砸。无论如何,使用这个标志来转换它们。* /
SDWebImageAvoidAutoSetImage 默认情况下,图像会在下载后添加到imageView中。但是在某些情况下,我们想要在设置图像之前有手(例如,应用一个过滤器或将它添加到交叉衰减动画中)使用这个标记如果你想在成功完成时手工设置图像
SDWebImageScaleDownLargeImages 默认情况下,图像会被解码,以尊重它们原来的大小。在iOS上,这一标志将把图像缩小到与设备受限内存兼容的大小。*如果“SDWebImageProgressiveDownload”标志设置禁用缩减。
*/
以上四个常用方法,点击进去查看内部实现代码时,你会发现所有方法都指向------>
源码注释解释的含义是
用url、占位符和自定义选项设置imageView图像。下载是异步的和缓存的。
@param url是图像的url。
@param占位符将首先设置的图像,直到图像请求完成。
@param选择在下载图像时使用的选项。
@参见SDWebImageOptions用于可能的值。
@param progressBlock在下载@note时,在后台队列
@param completedBlock的后台进程中执行进程块,该块是在操作完成时被调用的。这个块没有返回值,并将所请求的UIImage作为第一个参数。在出现错误时,图像参数为nil,第二个参数可能包含一个NSError。第三个参数是一个布尔值,指示是否从本地缓存或网络检索图像。第四个参数是原始图像url。
下面是图解(上面展示了每句话的备注)
1、设置展位图,并且取消当前下载任务
2、创建一个新的下载操作
3、下载操作代码(判断流是否存在,如果不存在则将其存在失效列表中,防止重复下载无效流)-----在这里他对NSString和NSURL的转换做了判断。原因是(非常常见的错误是使用NSString对象而不是NSURL发送URL。出于某种奇怪的原因,Xcode不会对这种类型的不匹配发出任何警告。在这里,我们通过允许url作为NSString传递来确保这个错误。)括号当中是文档给出的解释,所以这里做了强制转换。
4、利用唯一生成的key,到缓存--->内存---->磁盘中分别寻找。
5、寻找的顺序 缓存---->磁盘---->在没有就下载
下载流程之后就是清理缓存(种类) 1、清理所有内存缓存镜像 2、清理所有磁盘缓存镜像3、清理过期的缓存映像从磁盘中删除
/*
异步清除所有磁盘缓存映像。非阻塞方法-立即返回。@param完成一个应该在缓存过期后执行的块(可选)
注意:这里要注意[[SDImageCache sharedImageCache] clearDisk];方法会报错,下面clearDiskOnCompletion的方法会替代上面的方法
*/
[[SDImageCache sharedImageCache] clearDiskOnCompletion:^{
}];
/*
Clear all memory cached images --->清除所有缓存镜像
*/
[[SDImageCache sharedImageCache] clearMemory];
/*
异步将所有过期的缓存映像从磁盘中删除。非阻塞方法-立即返回。@param completionBlock在缓存过期后执行(可选)--->故名思义他是不能删除你当前缓存的大小的
*/
[[SDImageCache sharedImageCache] deleteOldFilesWithCompletionBlock:^{
}];
五、总结
SDWebImage加载图片的流程:
1. 入口 setImageWithURL:placeholderImage:options: 会先把 placeholderImage显示,然后 SDWebImageManager 根据 URL 开始处理图片。
2. 进入 SDWebImageManager-downloadWithURL:delegate:options:userInfo:,交给 SDImageCache 从缓存查找图片是否已经下载 queryDiskCacheForKey:delegate:userInfo:.
3. 先从内存图片缓存查找是否有图片,如果内存中已经有图片缓存,SDImageCacheDelegate回调 imageCache:didFindImage:forKey:userInfo: 到 SDWebImageManager。
4. SDWebImageManagerDelegate 回调 webImageManager:didFinishWithImage: 到 UIImageView+WebCache等前端展示图片。
5. 如果内存缓存中没有,生成 NSInvocationOperation添加到队列开始从硬盘查找图片是否已经缓存。
6. 根据 URLKey在硬盘缓存目录下尝试读取图片文件。这一步是在 NSOperation 进行的操作,所以回主线程进行结果回调 notifyDelegate:。
7. 如果上一操作从硬盘读取到了图片,将图片添加到内存缓存中(如果空闲内存过小,会先清空内存缓存)。SDImageCacheDelegate回调 imageCache:didFindImage:forKey:userInfo:。进而回调展示图片。
8. 如果从硬盘缓存目录读取不到图片,说明所有缓存都不存在该图片,需要下载图片,回调 imageCache:didNotFindImageForKey:userInfo:。
9. 共享或重新生成一个下载器 SDWebImageDownloader 开始下载图片。
10. 图片下载由 NSURLConnection来做,实现相关 delegate 来判断图片下载中、下载完成和下载失败。
11. connection:didReceiveData: 中利用 ImageIO做了按图片下载进度加载效果。
12. connectionDidFinishLoading: 数据下载完成后交给 SDWebImageDecoder 做图片解码处理。
13. 图片解码处理在一个 NSOperationQueue完成,不会拖慢主线程 UI。如果有需要对下载的图片进行二次处理,最好也在这里完成,效率会好很多。
14. 在主线程 notifyDelegateOnMainThreadWithInfo: 宣告解码完成,imageDecoder:didFinishDecodingImage:userInfo: 回调给 SDWebImageDownloader。
15. imageDownloader:didFinishWithImage: 回调给 SDWebImageManager告知图片下载完成
16. 通知所有的 downloadDelegates下载完成,回调给需要的地方展示图片。
17. 将图片保存到 SDImageCache中,内存缓存和硬盘缓存同时保存。写文件到硬盘也在以单独 NSInvocationOperation 完成,避免拖慢主线程。
18. SDImageCache 在初始化的时候会注册一些消息通知,在内存警告或退到后台的时候清理内存图片缓存,应用结束的时候清理过期图片。
19. SDWI 也提供了 UIButton+WebCache 和 MKAnnotationView+WebCache,方便使用。
20. SDWebImagePrefetcher 可以预先下载图片,方便后续使用。
原文链接:https://blog.csdn.net/qq_16146389/article/details/88355852