iOSiOS精华iOS 开发技巧大全

SDWebImage 源码分析

2015-11-22  本文已影响987人  mconintet

SDWebImage 源码分析

首先我 fork 了 SDWebImage 的源码,见 conintet/SDWebImage,这样在本文的链接中都是链到我的 fork 中,这么做的目的是防止将来 SDWebImage 代码发生变化导致本文的链接不准确。

有关 SD (SDWebImage 简称为 SD) 的使用方式还是得参考其 README 或者 wiki。本文只是阅读其源码的笔记。

图片下载

最先分析的就是图片下载部分的代码,因为这是最核心的功能。

因为 SD 在 UIImageView 上通过 Category 的方式增加了简单易用的 API,类似下面:

- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder;

于是通过几步 Jump to Definition 就可以发现,SD 的图片下载操作是由 SDWebImageDownloaderOperation 来完成的,于是看一下它的初始化方法:

- (id)initWithRequest:(NSURLRequest *)request
              options:(SDWebImageDownloaderOptions)options
             progress:(SDWebImageDownloaderProgressBlock)progressBlock
            completed:(SDWebImageDownloaderCompletedBlock)completedBlock
            cancelled:(SDWebImageNoParamsBlock)cancelBlock;

通过上面的方法签名,可以大概反向的知道:

  1. 使用了 NSURLRequest,那么很可能内部就使用的 NSURLConnection 来完成的下载
  2. 既然提供了 progresscompleted 这两个 callback,那么内部势必需要知道下载的进度
  3. 因为提供了 cancelled 这个 callback,那么内部的下载操作还需要可以取消

再看一下 SDWebImageDownloaderOperation 是继承于 NSOperation,因为下载是一个可以独立出来的计算单元,所以作为 Opreation 是很好理解的。然后在实际的图片下载中,为了下载的效率,下载的 Opreations 之间肯定是需要并发的。Operation 默认在其被调用的线程中是同步执行的,不过由于 Operation Queue 的存在,它可以将其中的 Operations 分别 attach 到由系统控制的线程中,而这些由系统控制的线程之间是并发执行的。

查看 SDWebImageDownloaderOperation 源码发现内部果然是使用的 NSURLConnection,那么由于需要提供 cancelled 的功能以及需要监听下载进度,故必须将 NSURLConnection 的实例配置成异步的方式:

具体代码在 L96

// 配置异步 NSURLConnection 的方式

// 实例化一个 NSURLConnection,并将自身(SDWebImageDownloaderOperation)设置为 NSURLConnection 实例的委托
self.connection = [[NSURLConnection alloc] initWithRequest:self.request delegate:self startImmediately:NO];
// 因为上一步的 startImmediately:NO,所以这里手动的触发 start
// 这样的效果和直接 startImmediately:YES 是一样的
[self.connection start];
// 因为上面两步结合起来或者直接 startImmediately:YES 的结果就是下载例程将会在当前 Run Loop 上以默认的模式进行调度,
// 而在 iOS 中除了主线程之外的线程都是默认没有运行 Run Loop 的,所以需要手动的运行一下
CFRunLoopRun();
// 之后的代码将会被 CFRunLoopRun() 所阻塞,这样 operation 所在的线程
// 就不会自动的退出,于是需要额外的代码在下载完成之后手动的停止 RunLoop 使得
// operation 所在的线程可以退出

对于下载进度的监听,SDWebImageDownloaderOperation 是通过将自身设置为 NSURLConnection 委托的形式完成的:

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response 在这一委托方法的实现中,SDWebImageDownloaderOperation 主要是获取服务端响应的 meta 信息,尝试根据响应的 statusCode 对下载过程进行预判,比如如果是 304 状态码直接从本地缓存中返回图片。但是这里的代码写的有些繁琐了,并且性能上也是存在些问题。首先可以看下这幅概览图:

为什么不能赋值?因为指针的捕获也是作为了 const,和基本类型一样。

总结起来说就是,objc 对象在 block 中捕获的是指向其真实地址的指针,指针以 const 的形式被捕获,不使用 __block 修饰就无法改变指针的内容,但是对于指针指向的对象,它们的内容还是可以改变的。

[2015-11-26 修正]
上面的关于 NSNotification 的说明有些纰漏,修正见 NSNotificationCenter

上一篇下一篇

猜你喜欢

热点阅读