-
Notifications
You must be signed in to change notification settings - Fork 0
7. SDWebImage原理
1.dispatch_barrier_sync函数,用于对操作设置顺序,确保在执行完任务后再确保后续操作。常用于确保线程安全性操作
2.NSMutableURLRequest:用于创建一个网络请求对象,可以根据需要来配置请求报头等信息
3.NSOperation及NSOperationQueue:操作队列是OC中一种告诫的并发处理方法,基于GCD实现,相对于GCD来说,操作队列的优点是可以取消在任务处理队列中的任务,另外在管理操作间的依赖关系方面容易一些,对SDWebImage中我们看到如何使用依赖将下载顺序设置成后进先出的顺序
4.NSURLSession:用于网络请求及相应处理
5.开启后台任务
6.NSCache类:一个类似于集合的容器,存储key-value对,这一点类似于nsdictionary类,我们通常用使用缓存来临时存储短时间使用但创建昂贵的对象。重用这些对象可以优化性能,因为它们的值不需要重新计算。另外一方面,这些对象对于程序来说不是紧要的,在内存紧张时会被丢弃
7.清理缓存图片的策略:特别是最大缓存空间大小的设置。如果所有缓存文件的总大小超过这一大小,则会按照文件最后修改时间的逆序,以每次一半的递归来移除那些过早的文件,直到缓存的实际大小小于我们设置的最大使用空间。
8.图片解压操作:这一操作可以查看SDWebImageDecoder.m中+decodedImageWithImage方法的实现。
9.对GIF图片的处理
10.对WebP图片的处理。
+-----------------------------------------------------------------+ | 🖼️ SDWebImage 加载流程图 | +-----------------------------------------------------------------+
[开始]
↓
┌──────────────── setImageWithURL:... ────────────────┐
│ (UIImageView+WebCache) │
│ → 先显示 placeholderImage │
└──────────────────────┬──────────────────────────────┘
↓
┌───────── downloadWithURL:... (SDWebImageManager) ─────────┐
│ → 检查缓存:调用 SDImageCache.queryDiskCacheForKey: │
└──────────────────────┬───────────────────────────────────┘
↓
+------------------------------+
| 缓存查找阶段 |
+------------------------------+
↓
┌────────────────────────────────────┐
│ 1. 内存缓存检查 │
│ → [SDImageCache objectForKey:key] │
└────────────────┬─────────────────┘
↓
┌─────────────┴────────────┐
↓ ↓
【命中内存缓存】 【未命中内存缓存】
↓ ↓
回调 delegate: 生成 Operation 进入队列
didFindImage: 开始硬盘查找(异步)
↓
[NSOperation: readDataFromDiskForKey:]
↓
尝试从磁盘读取图片文件
↓
┌─────────────────┴─────────────────┐
↓ ↓
【找到磁盘缓存】 【未找到任何缓存】
↓ ↓
把图片加入内存缓存 触发下载流程: 回调 didFindImage: 创建/复用 SDWebImageDownloader ↓ 使用 NSURLConnection 发起请求 ↓ connection:didReceiveResponse: ↓ connection:didReceiveData: → 支持渐进式解码(ImageIO) ↓ connectionDidFinishLoading: ↓ 交给 SDWebImageDecoder 解码(在后台线程) ↓ 解码完成 → 回主线程通知结果 ↓ imageDecoder:didFinishDecodingImage:... ↓ imageDownloader:didFinishWithImage:... ↓ webImageManager:didFinishWithImage:... ↓ 通知所有 delegate 下载成功 ✅ ↓ 保存图片到缓存(内存 + 磁盘)💾 (磁盘写入也通过 NSInvocationOperation 异步完成)
↑
│
┌─────────────┴────────────┐
↓ ↓
【展示最终图片】 【触发 completion 回调】
[imageView setImage:image] 可用于自定义处理
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 做了按图片下载进度加载效果。connectionDidFinishLoading: 数据下载完成后交给 SDWebImageDecoder 做图片解码处理。
12、图片解码处理在一个 NSOperationQueue 完成,不会拖慢主线程 UI。如果有需要对下载的图片进行二次处理,最好也在这里完成,效率会好很多。
13、在主线程 notifyDelegateOnMainThreadWithInfo: 宣告解码完成,imageDecoder:didFinishDecodingImage:userInfo: 回调给 SDWebImageDownloader。imageDownloader:didFinishWithImage: 回调给 SDWebImageManager 告知图片下载完成。
14、通知所有的 downloadDelegates 下载完成,回调给需要的地方展示图片。将图片保存到 SDImageCache 中,内存缓存和硬盘缓存同时保存。写文件到硬盘也在以单独 NSInvocationOperation 完成,避免拖慢主线程。
15、SDImageCache 在初始化的时候会注册一些消息通知,在内存警告或退到后台的时候清理内存图片缓存,应用结束的时候清理过期图片。
16、SDWI 也提供了 UIButton+WebCache 和 MKAnnotationView+WebCache,方便使用。
17、SDWebImagePrefetcher 可以预先下载图片,方便后续使用。
+-----------------------------+ | 开始缓存图片 | +-----------------------------+ ↓ +-----------------------+ | 1. 写入内存缓存 | | [Memory: NSCache] | | 快速访问,程序内有效 | +-----------------------+ ↓ +-----------------------+ | 2. 是否需磁盘缓存? | | 根据策略决定(默认开启) | +-----------------------+ ↓ yes +----------------------------------+ | 3. 添加 task 到串行 I/O 队列 | | dispatch_async(ioQueue, ^{...}) | +----------------------------------+ ↓ +----------------------------------+ | 4. 判断图片类型 | | → JPEG? PNG? 是否有 Alpha? | +----------------------------------+ ↓ +----------------------------------+ | 5. 转换为对应格式的 NSData | | UIImageJPEGRepresentation() | | UIImagePNGRepresentation() | +----------------------------------+ ↓ +----------------------------------+ | 6. 对 key 做 MD5 得到安全文件名 | | key = "http://..." | | → filename = "d41d8cd..." | +----------------------------------+ ↓ +----------------------------------+ | 7. 写入磁盘 | | [data writeToFile:path atomically:YES] | +----------------------------------+ ↓ ✅ 缓存完成!
设计选择 | 原因 内存 + 磁盘双缓存 | 平衡速度与持久性 串行队列写磁盘 | 防止并发写冲突,保护 I/O 性能 自动识别格式 | 保持画质同时节省空间 MD5 文件名 | 安全、统一、兼容性强 异步写磁盘 | 不阻塞主线程,UI 流畅