SDWebImage 源码阅读:从头开始(1)

版本:1.0
主要用途:提供一个 UIImageView 的分类,支持从网络获取图片

功能提供:

  1. 一个添加了网络图片和缓存管理的 UIImageView 分类
  2. 使用 NSOperation 处理的异步图片下载器
  3. 异步的内存与磁盘缓存过期自动处理
  4. 确保同一个 URL 不会被多次下载
  5. 确保虚假 URL 不会被重复处理
  6. 确保性能

在 ReadMe 里,作者提到,当时(1.0 版本发布于 2009 年)没有特别完善的这个方面的集成库,在个人解决方案探索下,他首先使用了 NSURLConnection 来进行下载,但是发现比 youtube 慢个 10 倍,而且,NSURLConnection 貌似不能做到完全的异步,会被UI操作打扰。这个暂时存疑,有待验证。(作者在2.0里推翻了这个说法orz)

SDWebImage 源码阅读:从头开始(1)_第1张图片
1.0 所含内容

如上图,1.0 版本其实是一个很简单的库,很容易看懂。主要包括一个下载器、一个管理类、一个缓存类和一个分类接口。

-->UIImageView+WebCache

其主要内容是:

- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder
{
     SDWebImageManager *manager = [SDWebImageManager sharedManager];

    // Remove in progress downloader from queue
    [manager cancelForDelegate:self];

    UIImage *cachedImage = [manager imageWithURL:url];

    if (cachedImage)
    {
        self.image = cachedImage;
    }
    else
   {
        if (placeholder)
       {
            self.image = placeholder;
        }

       [manager downloadWithURL:url delegate:self];
    }
}

这个函数就是我们调用这个库的接口,很方便。而这个函数做的事情包括:

  1. 移除下载器中还在下载中的任务
  2. 取出管理器中缓存的图片
  3. 如果有缓存,则赋值给目标;否则调用预览图,并开始下载

显然,前两步是对第三步的补充。当第一次使用这个函数,调用的肯定是

[manager downloadWithURL:url delegate:self];

那么,我们来进入管理器,看看管理器做了些什么。

-->SDWebImageManager

管理器提供四个接口:

+ (id)sharedManager;
- (UIImage *)imageWithURL:(NSURL *)url;
- (void)downloadWithURL:(NSURL *)url delegate:(id)delegate;
- (void)cancelForDelegate:(id)delegate;

第一个接口是返回一个类实例,也就是说,这里用到单例模式,整个项目中,我们只需要一个管理器;
第二个接口是根据 url 返回相应的图片;
第三个是重点:根据 url 下载图片;
第四个是取消代理,用来取消下载。

在管理类中,有四个可变数组:分别用来管理代理、下载器、与相应 url 绑定了的下载器和无效 url。在

 - (void)downloadWithURL:(NSURL *)url delegate:(id)delegate;

中,我们所做的事情是:如果该 url 已经绑定过一个下载器,取出该下载器;否则新建一个下载器;然后将下载器和相应的代理分别加入数组中。

这中间的关键之处:新建一个下载器,即

downloader = [SDWebImageDownloader downloaderWithURL:url delegate:self];

这个就需要去下载器类看看了。

-->SDWebImageDownloader

这个类继承自 NSOperation,提供两个类接口:

+ (id)downloaderWithURL:(NSURL *)url delegate:(id)delegate;
+ (void)setMaxConcurrentDownloads:(NSUInteger)max;

第二个接口用来设置当前最大下载数,第一个接口是我们要看的重点。这里才是真正下载操作进行的地方。

首先我们理解一下 NSOperation 这个类,这个类是一个多线程解决方案,也就是说拿这个下载可以是异步的,这样就不妨碍我们进行其他的软件操作。步骤大概是:将操作封装到 NSOperation 中,然后将 NSOperation 添加到 NSOperationQueue 中,系统就会自动将 NSOperation 中的操作放到新线程中执行。

在 main 中是我们封装的下载操作:下载图片;当下载操作没有被取消时,坚持下载是否完成。这其中的第二步,我们通过代理,让管理器 SDWebImageManager 帮我们完成函数的实现。

现在回到这个问题:当我们调用

+ (id)downloaderWithURL:(NSURL *)url delegate:(id)delegate;

后,发生了什么?答案是:创建下载器,并放入下载队列中。

+ (id)downloaderWithURL:(NSURL *)url delegate:(id)delegate
{
    SDWebImageDownloader *downloader = [[[SDWebImageDownloader alloc] init] autorelease];
    downloader.url = url;
    downloader.delegate = delegate;

    if (downloadQueue == nil)
   {
        downloadQueue = [[NSOperationQueue alloc] init];
        downloadQueue.maxConcurrentOperationCount = 8;
   }

    [downloadQueue addOperation:downloader];

    return downloader;
}

那么,现在我们再回到 SDWebImageManager,来看看如何判断图片的下载是否完成。

-->SDWebImageManager

直接看代码,思路为:

  1. 先遍历下载器数组,找到当前需要检查的下载器;如果图片已存在,则下载完成,在 UIImageView+WebCache 中直接赋值即可;否则取消下载
  2. 然后,如果下载成功,以 url 为 key 值,缓存下来;下载失败,则将该 url 放入无效 url 数组

其中,有一个缓存图片的操作,涉及到最后一个类:

-->SDImageCache

图片是被缓存至磁盘上的,使用的是:

[[NSFileManager defaultManager] createFileAtPath:path contents:UIImageJPEGRepresentation(image, (CGFloat)1.0) attributes:nil];

当然,这个类还包括对磁盘容量的监听与内存的释放。这部分暂时略过,看一看就可以了。


以上,基本上按照从尾到头的顺序,顺着思路捋了一遍代码,1.0 版本内容不多,也就看完啦~

参考资料:
SDWebImage 在 Github 上的早期 release 版本
多线程编程2-NSOperation

你可能感兴趣的:(SDWebImage 源码阅读:从头开始(1))