前端时间完成云相册经常使用到 SDWebImage
来进行图片加载,但对于图片加载过程中怎么样实现不会深究。下面小编就对 SDWebImage
进行相应的分析:
SDWebImage
的下载器
SDWebImage
的下载器是 SDWebImageDownloader
利用单例模式sharedDownloader
,可以对下载的图片进行相关配置。
可以配置的部分如下:
- 下载选项
- HTTP的头部
- 压缩、下载超时、下载顺序、最大并发数等
下载选项
1 | typedef NS_OPTIONS(NSUInteger, SDWebImageDownloaderOptions) { |
HTTP的头部设置
1 |
|
我们可以通过上述 forHTTPHeaderField
的参数进行相应 HTTPheader
的设置,使用者可以对头部信息进行相关的添加或者是删除 HTTP
头部信息。
线程安全
在图片下载过程中我们要保线程访问的安全性,barrierQueue
是实现网络响应的序列化实例。
1 | // This queue is used to serialize the handling of the network responses of all |
在保证线程安全的起见,我们对于 URLCallbacks
进行增改都需要放在dispatch_barrier_sync
的形式放入到 barrierQueue
。
但是如果我们只要进行相关的查询那就使用 dispatch_sync
放入 barrierQueue
中即可。
1 | __block NSArray *callbacksForURL; |
回调
在我们下载图片的过程中,每一张图片都需要开启一个线程,在每一个线程中都需要对执行一定的回调信息。这些回调的信息会以 block
的实行出现:
1 | typedef void(^ SDWebImageDownloaderProgressBlock)(NSInteger receivedSize, |
图片下载的这些回调信息存储在 SDWebImageDownloader
类的 URLCallbacks
属性中,该属性是一个字典,key
是图片的 URL
地址,value
则是一个数组,包含每个图片的多组回调信息。
下载器
整个下载过程中我们需要执行在本小结讲述的下载器中进行,下载器对于下载的管理都是放在-(id <SDWebImageOperation>)downloadImageWithURL:
中的:
1 | - (id <SDWebImageOperation>)downloadImageWithURL:(NSURL *)url options:(SDWebImageDownloaderOptions)options progress:( SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageDownloaderCompletedBlock)completedBlock { |
我们在上面的方法中调用的方法 -(void)addProgressCallback:completedBlock:forURL:createCallback:
将在访问图片请求的信息直接放入下载器。
1 | - (void)addProgressCallback:( SDWebImageDownloaderProgressBlock)progressBlock completedBlock:( SDWebImageDownloaderCompletedBlock)completedBlock forURL:(NSURL*)url createCallback:( SDWebImageNoParamsBlock)createCallback { |
下载操作
在每张图片下载过程中都要调用一次具体的操作都会调用 Operation
,下面就分析一下中间的具体过程。
我们打开 SDWebImage
的文件夹可以到其中有一个 SDWebImageOperation
的类,如下:
1 |
|
其中我们使用 NSOpation
的子类来完成具体图片下载的过程,这个类就是 SDWebImageDownloaderOperation
。在 SDWebImageDownloaderOperation
类中继承 NSOperation
的类而且实现 SDWebImageOperation的cancel
的取消协议。除了继承而来的方法,该类只向外暴露了一个方法,即上面所用到的初始化方法-initWithRequest:options:pregress:completed:cancelled:
。
对于图片的下载, SDWebImageDownloaderOperation
完全依赖于 URL
加载系统中的 NSURLConnection
类(并未使用 iOS7
以后的 NSURLSession
类)。我们先来分析一下 SDWebImageDownloaderOperation
类中对于图片实际数据的下载处理,即 NSURLConnection
各代理方法的实现。
首先, SDWebImageDownloaderOperation
在 Extention
中采用了NSURLConnectionDataDelegate
协议,并实现了协议的以下几个方法:
1 | connection:didReceiveResponse: |
这些方法我们就不逐一分析了,就终点分析一下 connection:didReceiveResponse:
和 connection:didReceiveData:
两个方法。
connection:didReceiveResponse
方法通过判断 NSURLResponse
的实际类型和状态码,对除 304
以外 400
以内的状态码反应。
1 | - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response { |
connection:didReceiveData:
方法的主要任务是接受数据。每次接收到数据时,都会用现有的数据创建一个CGImageSourceRef
对象以作处理。在首次获取到数据时(width+height==0
)会从这些包含图像信息的数据中取出图像的长、宽、方向等信息以备使用。而后在图片下载完成之前,会使用 CGImageSourceRef
对象创建一个图像对象,经过缩放、解压缩操作后生成一个 UIImage
对象供完成回调使用。当然,在这个方法中还需要处理的就是进度信息。如果我们有设置进度回调的话,就调用进度回调以处理当前图片的下载进度。
1 | - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data |
我们前面说过 SDWebImageDownloaderOperation
类是继承自 NSOperation
类。它没有简单的实现 main
方法,而是采用更加灵活的 start
方法,以便自己管理下载的状态。
在 start
方法中,创建了我们下载所使用的 NSURLConnection
对象,开启了图片的下载,同时抛出一个下载开始的通知。start
方法的具体实现如下:
1 | - (void)start { |
在下载完后或者是下载失败后都会停止当前调用的 runloop
,清楚链接随后就抛出下载停止的消息。
如果下载成功,则会处理完整的图片数据,对其进行适当的缩放与解压缩操作,以提供给完成回调使用。具体可参考-connectionDidFinishLoading:
与 -connection:didFailWithError:
的实现。
总结
我们在上面的介绍可以看出在下载图片的过程中,每次下载图片都会调用 NSOperation
的函数进行处理,每次数据实际实现是使用 NSURLConnection
。我们把具体实现的线程放置在队列中进行执行操作。如果下载成功,则会处理完整的图片数据,对其进行适当的缩放与解压缩操作,以提供给完成回调使用。具体可参考-connectionDidFinishLoading:
与 -connection:didFailWithError:
的实现。