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)

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<SDWebImageManagerDelegate>)delegate;
- (void)cancelForDelegate:(id<SDWebImageManagerDelegate>)delegate;

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

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

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

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

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

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

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

-->SDWebImageDownloader

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

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

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

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

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

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

+ (id)downloaderWithURL:(NSURL *)url delegate:(id<SDWebImageDownloaderDelegate>)delegate;

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

+ (id)downloaderWithURL:(NSURL *)url delegate:(id<SDWebImageDownloaderDelegate>)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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 222,252评论 6 516
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,886评论 3 399
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 168,814评论 0 361
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,869评论 1 299
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,888评论 6 398
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,475评论 1 312
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 41,010评论 3 422
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,924评论 0 277
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,469评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,552评论 3 342
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,680评论 1 353
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,362评论 5 351
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 42,037评论 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,519评论 0 25
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,621评论 1 274
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 49,099评论 3 378
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,691评论 2 361

推荐阅读更多精彩内容