最近阅读学习了SDWebImage源码。参照其思路实现了一个极简的版本。下面主要是描述一下各个模块的实现思路和需要注意的细节。
项目地址:HYLWebImage
一、图片下载类的实现思路
- 客户端通过URL向服务器请求图片,通过苹果NS类的网络请求异步加载
- 将返回的image放在UIImageView控件上展示
- 使用NSURLSession或者NSData来下载
需要注意的点:
- 异步请求成功后切换到主线程(参考了SDWebImage的主线程宏)
#define dispatch_main_async_safe(block)\
if (strcmp(dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL), dispatch_queue_get_label(dispatch_get_main_queue())) == 0) {\
block();\
} else {\
dispatch_async(dispatch_get_main_queue(), block);\
}
复制代码
- block闭包的使用
- 多添加对非正常值的判断
- 请求超时的解决策略
- 请求的优先级
二、线程管理类的实现思路
- 维护两个队列:
- 下载队列
- 预下载队列
- 根据max值的大小来确定Downloader进入哪个队列,限制最大并发量。max值可开放为接口,便于自定义
- 需要在各种状态下(比如完成了一个Downloader)判断下载队列的值是否 < max && 预下载队列是否有值,如果都是true,则把预下载队列的Downloader移入下载队列
需要注意的点:
- manager类需要通过单例只初始化一次,保持一个全局状态
- Downloader根据条件加入哪一个队列
- 下载队列发生变化后,检查一次条件(max和预下载队列的值)
三、缓存类的实现思路
- 下载完后将图片放入内存;
- 将最大缓存的数量和是否不使用缓存开放为接口,便于自定义(暂不从图片大小考虑)
- 提供本地文件读写的方法
- 根据runtime,动态的存入内存
- 优先存入内存,超过的最大值则存入本地
- 优先从内存读取,没有则才从本地读取
- 从本地存储读取的数据时,如果当前最大内存数没有满,则放入内存
需要注意的点:
- 与线程管理类的交互,主要是在线程管理类里适当的位置写入和读取
- 内存设计需要考虑的问题:
- 内存存储的空间大小,避免内存压力过大
- 与上一条对应的,需要有删除淘汰策略
- 以队列的方式删除:先进先出
- LRU算法:比如30分钟内是否被使用过(最近最久未使用算法)
- 每次读写时遍历检查
- 前后台切换时遍历检查
- 如果上次使用时间和当下时间超过三十分钟,那么移除图片
- 本地存储需要考虑的问题:
- 读取效率要比内存低,要有存储方式的选择
- 尽管空间相对大,但也要有大小的限制
- 本地存储的淘汰删除策略:
- 超过几天后是否删除
四、UIImageView分类的实现思路
- 在UIImageView的分类里调用Manager管理类即可
- 分类的变量用runtime的关联变量实现
- 把一个downloader下载类存入关联变量,在一个下载周期进行删除和添加操作,避免多个下载任务
五、总体的实现思路
核心流程是manager这样一个管理类来调度downloader下载类和cache缓存类。当一个任务开始,manager管理类先检查是否有内存缓存,然后再检查是否有本地缓存。如果有则用缓存的图片,如果没有,就调用downloader下载,下载成功后,再放入缓存,最后显示图片。
六、还没有做的功能
- 图片解码和压缩功能
- URL的合法性校验
- 检查是否有还未完成的下载
- …………
七、补充一个对图片解码实现时的注意点
- 对不同格式的图片,解码采用不同的方法,这就是需要应用设计模式里的【策略模式】
- 在哪个阶段做图片解码处理,这涉及到职责分工的问题
- 从本地存储读取后,解码
- 网络请求返回后,解码