设为首页 加入收藏

TOP

BBWebImage 设计思路(一)
2019-08-26 07:10:14 】 浏览:89
Tags:BBWebImage 设计 思路

BBWebImage 设计思路

BBWebImage 是高性能 Swift 图片组件,用于图片下载、缓存、编解码、编辑与展示。

GitHub 地址: https://github.com/Silence-GitHub/BBWebImage

效果图

下载、展示并缓存原图

下载、渐进式解码、编辑图片,缓存编辑后的图片至内存 (Memory)、缓存原图数据至磁盘 (Disk)

  • 添加滤镜

  • 绘制圆角、边框

性能对比

测试的图片组件有 BBWebImage (1.1.0)、SDWebImage (4.4.6 以及 FLAnimatedImage 1.0.12 用于测试 GIF)、YYWebImage (1.0.5) 和 Kingfisher (4.10.1)。测试设备是 iPhone 7,iOS 12.1。

  • BBWebImage 的内存缓存与磁盘缓存速度很快,针对缩略图的优势明显

  • 加载展示 GIF,BBWebImage 占用很少的 CPU 和内存

为什么写这个图片组件

写 BBWebImage 最开始的目的是要解决现有图片组件中图片编辑与动图的问题。

做过的项目中,图片组件都主要用 SDWebImage,显示 WebP、APNG 等格式动图用 YYWebImage。这些图片组件都非常优秀,能满足大多数使用场景的需求。YYWebImage 支持的图片格式很多,但是功能和可自定义程度不如 SDWebImage (例如自定义图片解码器)。当 BBWebImage 第一版 0.1.0 发布时,SDWebImage 的最新正式版是 4.4.3,还没有图片编辑模块。有些时候需要展示编辑后的图片,例如添加滤镜、绘制圆角和边框 (防止 CALayer 设置圆角造成顿卡) 等,也需要缓存编辑后的图片。如果用 SDWebImage 下载图片并编辑,会有以下问题:

  1. 如果只缓存编辑后的图片,则展示原图需要再次下载。
  2. 假设原图数据缓存至磁盘。如果不缓存编辑后的图片,需要在每次展示前重复编辑原图这个步骤。如果把编辑后的图片缓存至内存和磁盘,为了与原图区分,需要维护 cache key (在缓存中一个 key 对应原图,另一个 key 对应编辑后的图片)。如果把编辑后的图片只缓存至内存,则为了区分从缓存中取出的图片是否经过编辑,需要判断是从内存还是磁盘取到的图片。
  3. 如果用 Core Graphics 框架编辑图片,SDWebImage 的图片解压缩 (Decompress) 是不必要的。编辑和解压缩步骤类似:创建 CGContext、绘制图片、创建新图片。需要在编辑前禁用图片解压缩,完成后再启用。

另外,SDWebImage 的图片降采样 (Downsample) 用了统一处理的方式,图片分辨率大于固定阈值是降采样的必要条件。问题就在于阈值是固定的,遇到多张大图的情况,这个阈值还是太大,导致内存占用过多而崩溃。如果图片组件中有图片编辑模块,可以把图片降采样放入编辑模块,就可以自定义降采样参数,从而解决内存占用过多问题。

关于动图,SDWebImage 用 FLAnimatedImage 来展示 GIF,但是有性能问题。原因是 FLAnimatedImage 没有继承 UIImage,SDWebImage 的解码器无法直接返回 FLAnimatedImage,只好在主线程中用图片数据创建 FLAnimatedImage,这一步阻塞主线程导致顿卡。具体代码分析和解决方案参见 SDWebImage 加载显示 GIF 与性能问题。解决方案能用,但是从设计的角度看,SDWebImage 使用 FLAnimatedImage 并不合适。FLAnimatedImage 只适用于 GIF,无法通过自定义解码器来支持其他格式的动图。理想的情况是,图片组件搭建好展示动图的框架,有常用动图的解码,可以自定义解码器来支持其他格式的动图。

架构设计

主要结构

BBWebImage 的主要结构可以看下面这幅图。BBImageCache 管理图片缓存,BBImageDownloader 管理图片下载,BBImageCoderManager 管理图片编解码,BBWebImageEditor 提供图片编辑方法。BBWebImageManager 调用前四者的方法实现相应功能,对外提供一个方法实现图片加载 (缓存读取与下载)、解码、编辑和缓存。UIImageView 的扩展方法调用 BBWebImageManager 的方法获取图片用于展示。动图封装成 BBAnimatedImage,用 BBAnimatedImageView 展示。

BBImageCache

BBImageCache 是图片缓存协议,定义向缓存存取图片的行为。BBLRUImageCache 是默认使用的缓存,遵循 BBImageCache 协议。BBLRUImageCache 里面有内存缓存与磁盘缓存,都采用 LRU 算法 (Least recently used)。这部分设计基本参照 YYCache。内存缓存用字典和双向链表实现 LRU 算法。磁盘缓存用 SQLite 数据库存储数据相关信息 (key、大小、更新时间等),二进制数据本身根据文件大小来决定存储至 SQLite 数据库或者直接写入沙盒目录。

往内存缓存中保存的是 UIImage,取出的也是 UIImage。往磁盘缓存中存储的是 Data 或者是 UIImage,后者会被编码成 Data;取出的只是 Data,这里不会进行解码 (BBWebImageManager 拿到数据,才会用 BBImageCoderManager 进行解码)。

如果默认缓存无法满足需求,可以自定义缓存,遵循 BBImageCache 协议,替换默认缓存。

BBImageDownloader

BBImageDownloader 是图片下载协议,定义图片下载行为。BBMergeRequestImageDownloader 是默认使用的下载器,遵循 BBImageDownloader 协议。BBMergeRequestImageDownloader 会合并对同一 URL 的网络请求,防止对同一 URL 发出重复请求。每一个下载任务封装成 BBImageDownloadTask (是个协议,默认实现是 BBImageDefaultDownloadTask,可自定义实现) ,包含这次下载任务的完成回调等信息。每一个 URL 网络请求 (以下称为 "下载操作") 封装成 BBImageDownloadOperation (也是协议,默认实现是 BBMergeRequestImageDownloadOperation,可自定义实现),包含至少一个下载任务。

下载操作的执行顺序是,一般操作 (下载图片后要立即使用) 优先于预加载操作 (图片不需要在下载后立即使用,只是下载存入缓存),同时先进先出,也就是老的操作优先执行。虽然 SDWebImage 提供了后进先出和设置优先级的功能,但在做过的项目中并没有用到。因此这里没有设计这些功能,以后需要的话可以加上。实现方法原来是用自带的 Operation 和 OperationQueue 实现,但后来想把这一部分也自定义,于是用字典和双向链表实现。一共有两组字典和双向链表的组合,一组代表一般操作队列,另一组代表预加载操作队列。最多同时执行操作数为 6 个。操作数少于 6,有新操作进来就执行;大于等于 6,把新操作插入相应队列尾部。一个操作结束后,先从一般操作队列头部取一般操作来执行,没有的话再从

首页 上一页 1 2 3 下一页 尾页 1/3/3
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇OC 知识:彻底理解 iOS 内存管理.. 下一篇iOS——调试工具LLDB学习

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目