胡说八道 - 3 SDWebImage 见语

SDWebImage 简要介绍

SDWebImage 是一款性能卓越、流行度高的网络图片下载框架。对 SDWebImage 框架代码解读的文章数不胜数,这里只胡说八道框架的设计思想。
[注]:推荐南峰子的博客 《源码解析:SDWebImage 实现分析》
《计算机组成与设计:硬件/软件接口》 CPU 和 存储管理讲的很好。第三版、第四版均可。

框架设计思想剖析

SDWebImage 加载图片的流程

流程参考这里 《那些著名或非著名的 iOS 面试题 - 前编》

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 在初始化的时候会注册一些消息通知,在内存警告或退到后台的时候清理内存图片缓存,应用结束的时候清理过期图片。

总结

总体设计思路

  1. 内存中维护一个图片缓存作为一级缓存,辅存中维护一个图片缓存作为二级缓存。同时共享的下载器缓存下载任务,减少下载时的资源消耗。进行文件查找、文件下载、图片解码、文件读写操作等都使用后台线程完成,减少对程序性能的影响。

设计经验

  1. 设计模式
    Observer、Delegate 、Singleton 的混合使用,使各功能组件的配合更加高效。这些简单的设计模式也没有那么不堪,关键是不乱套、不滥用。
  2. 性能优化
    1. 功能明确分开
      文件查找、文件下载、文件读写、图片解码等操作都交由对应的功能组件完成,代码清晰,结构明确。
  3. 多线程的使用
    处理必要的 UI 数据交换,其他的操作都尽可能的在后台线程完成,减少对主线程的影响。
  4. 架构设计
  5. 多级缓存的灵活使用
    内存中缓存图片作为一级缓存,辅存中缓存图片作为二级缓存;还对可能出现资源损耗的下载操作实现一次缓存。
    [注] 缓存的使用不是越多多好,请慎重选择使用。
  6. 功能组件的设置
    将功能组件分开,各司其职。做到代码结构清晰,功能有条不紊。(这里将‘文件下载’、‘文件IO’的操作称为功能组件并不合适,只是为了便于理解思路。)

题外话

  1. 多线程
    什么时候应该使用多线程?
  2. 完成线程或者任务间的同步操作
  3. 减少多主线程的性能影响,提高反应速度。使用后台线程。

多线程中应该考虑什么?

  1. 添加什么方式的任务到队列中?同步方式还是异步方式?(同步任务和异步任务的区别是什么?为是否阻塞当前线程,以同步方式添加任务到队列中会阻塞当前线程)

  2. 怎么去执行线程?串行还是并行?

3. 线程安全的?
典型的解决线程安全问题的方法:使用线程锁实现。对应各有优缺点。
这里推荐解决线程安全问题几篇文章:1.Objc 线程安全类的设计系列文章 2.多线程开发之线程安全 3.简书上的 iOS 多线程开发-线程安全

[注]1. 一个典型的 GCD 死锁问题
// 1 打印当前线程号
NSLog("之前 - %@", NSThread.currentThread())
// 2 在当前线程中,向主队列中添加一个同步任务
dispatch_sync(dispatch_get_main_queue(), { () -> Void in
NSLog("sync - %@", NSThread.currentThread())
})
// 3 没有切换线程,在主线程中打印
NSLog("之后 - %@", NSThread.currentThread())
结果:只能输出:之前 - XX
步骤分解:
1 在主线程中先获取主线程队列,以同步的方式添加一个任务到主线程队列中。
2 主线程队列中的任务会被取出串行执行,同时因为是同步方式,所以会阻塞当前主队列线程
3 主线程被阻塞,不能被执行
[注] 2. 实现线程间数据同步或者通信
两种实现方式:GCD 实现 和 NSOperation 实现。

  1. 缓存
    强烈推荐阅读《计算机组成与设计:硬件与软件接口》
  2. 架构
    好的架构是一步一步进化出来的,而不是一次就完成所有架构的。但是不可否认,编写代码时,应该遵循良好的编码规范和设计方法

小技巧

  1. 不同网络状态下的图片加载处理
    场景:3/4G 和 WiFi 环境下的高清图片和普通质量图片的显示问题
    SDWebImage 会默认加载缓存中的高清图片,为了节省用户的流量,应该对网络状态进行判断,控制加载不同质量的图片。但是这还没有完,有些用户默认设置在 3/4 G 网络环境下加载普通质量的图片(设置在偏好设置中),该怎么进行省流处理?
    例子:《iOS 开发-你真的会用 SDWebImage?》
  2. 下载进度展示
    场景:正在下载的 GIF 图片,被点击浏览大图,怎么保证小图和大图中显示的下载进度是一致的?
  3. SDWebImage 设置图片圆角,避免离屏渲染
    场景:设置图片圆角,既要快,又不能有离屏渲染。
    key: 在 ImageView 的分类中,开启后台线程使用 Q2D 进行图片绘制。

任务

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

推荐阅读更多精彩内容