iOS第三方 - SDWebImage
SDWebImage
1 - SDWebImage 是一个十分经典的 iOS 获取网络图片的类库,它通过各种缓存的方式把图片保存在内存、磁盘等位置,并且在类库内做好的对缓存的管理。在最近的版本中,也开放了一些让用户可以管理 cache 的接口
2 - SDWebImage 图片缓存有两种方式:内存和磁盘两种存储方式。它提供了很多的方法,简单来说就是:根据 key 来管理 image(删除、存储、清除所有缓存、获取文件缓存等等)
3 - SDWebImage 加载图片流程
① 入口 setImageWithURL:placeholderImage:options: 会先把 placeholderImage 显示,然后 SDWebImageManager 根据 URL 开始处理图片:进入 SDWebImageManager 的 downloadWithURL:delegate:options:userInfo: 方法开始下载
② 在下载之前,先交给 SDImageCache 从缓存查找图片是否已经下载 queryDiskCacheForKey:delegate:userInfo,流程如下
先从内存图片缓存查找是否有图片
如果内存中已经有图片缓存,SDImageCacheDelegate 回调 imageCache:didFindImage:forKey:userInfo: 到 SDWebImageManager,然后 SDWebImageManagerDelegate 回调 webImageManager:didFinishWithImage: 到 UIImageView+WebCache 等展示图片
如果内存缓存中没有,则生成 NSInvocationOperation,开始从硬盘查找图片是否已经缓存:根据 URLKey 在硬盘缓存目录下尝试读取图片文件(这一步是在 NSOperation 进行的操作,所以回主线程进行结果回调 notifyDelegate)如果上一操作从硬盘读取到了图片,将图片添加到内存缓存中(如果空闲内存过小,会先清空内存缓存),SDImageCacheDelegate 转到 imageCache:didFindImage:forKey:userInfo: 展示图片
如果从硬盘缓存目录读取不到图片,说明所有缓存都不存在该图片,回调 imageCache:didNotFindImageForKey:userInfo。紧接着共享或重新生成一个下载器 SDWebImageDownloader 开始下载图片
③ 图片下载由 NSURLConnection 来做,实现相关 delegate 来判断图片下载中、下载完成和下载失败
connection:didReceiveData: 中利用 ImageIO 做了按图片下载进度加载效果
connectionDidFinishLoading: 数据下载完成后交给 SDWebImageDecoder 做图片解码处理:图片解码处理在一个 NSOperationQueue 完成,不会拖慢主线程 UI。如果有需要对下载的图片进行二次处理,最好也在这里完成,效率会好很多。在主线程 notifyDelegateOnMainThreadWithInfo: 宣告解码完成,imageDecoder:didFinishDecodingImage:userInfo: 回调给 SDWebImageDownloader,最后imageDownloader:didFinishWithImage: 回调给 SDWebImageManager 告知图片下载完成,通知所有的 downloadDelegates 下载完成,回调给需要的地方展示图片
③ 下载完毕之后,将图片保存到 SDImageCache 中(内存缓存和硬盘缓存同时保存)写文件到硬盘也在以单独 NSInvocationOperation 完成,避免拖慢主线程
注:SDImageCache 在初始化的时候会注册一些消息通知,在内存警告或退到后台的时候清理内存图片缓存,应用结束的时候清理过期图片
SDWebImage 也提供了 UIButton+WebCache 和 MKAnnotationView+WebCache,方便使用。SDWebImagePrefetcher 可以预先下载图片,方便后续使用
4 - SDWebImage 主要用到的对象
① UIImageView (WebCache):入口封装,实现读取图片完成后的回调
② SDWebImageManager:对图片进行管理的中转站,记录那些图片正在读取
③ SDImageCache:根据 URL 的 MD5 摘要对图片进行存储和读取(实现存在内存中或者存在硬盘上两种实现)
④ SDWebImageDownloader:根据 URL 向网络读取数据(实现部分读取和全部读取后再通知回调两种方式)
⑤ SDWebImageDecoder:异步对图像进行了一次解压
5 - SDImageCache 是怎么做数据管理的
① SDImageCache 分两个部分:内存层面和硬盘层面。内存层面的相当是个缓存器,以 Key-Value 的形式存储图片。当内存不够的时候会清除所有缓存图片
用搜索文件系统的方式做管理,文件替换方式是以时间为单位,删除时间大于一周的图片文件
当 SDWebImageManager 向 SDImageCache 要资源时,先搜索内存层面的数据,如果有直接返回,没有的话去访问磁盘,将图片从磁盘读取出来,然后做 Decoder,将图片对象放到内存层面做备份,再返回调用层
2 - 为什么必须做 Decoder
① 由于 UIImage 的 imageWithData 函数是每次画图的时候才将 Data 解压成 ARGB 的图像,所以在每次画图的时候,会有一个解压操作,这样效率很低,但是只有瞬时的内存需求
② 为了提高效率通过 SDWebImageDecoder 将包装在 Data 下的资源解压,然后画在另外一张图片上,这样这张新图片就不再需要重复解压,这种做法是典型的空间换时间的做法
6 - 如何使用 SDWebImage
① options 参数
1 SDWebImageRetryFailed = 1 << 0, // 失败后重试 2 SDWebImageLowPriority = 1 << 1, // UI 交互期间开始下载,导致延迟下载.比如 UIScrollView 减速 3 SDWebImageCacheMemoryOnly = 1 << 2, // 只进行内存缓存 4 5 SDWebImageProgressiveDownload = 1 << 3, // 这个标志可以渐进式下载,显示的图像是逐步在下载 6 SDWebImageContinueInBackground = 1 << 5,// 后台下载 7 SDWebImageRefreshCached = 1 << 4, // 刷新缓存 8 9 SDWebImageHandleCookies = 1 << 6, // NSMutableURLRequest.HTTPShouldHandleCookies = YES; 10 SDWebImageAllowInvalidSSLCertificates = 1 << 7, // 允许使用无效的SSL证书 11 SDWebImageHighPriority = 1 << 8, // 优先下载 12 SDWebImageDelayPlaceholder = 1 << 9, // 延迟占位符 13 SDWebImageTransformAnimatedImage = 1 << 10, // 改变动画形象
② 代码示例:在 tableView 中展示从网络上下载的图片
// - ImageCell.h
1 #import <UIKit/UIKit.h> 2 @interface ImageCell : UITableViewCell 3 4 @property(nonatomic,strong)UIImageView *aImageView;// 展示图片 5 @property(nonatomic,strong)UILabel *numberLabel; // 显示标题 6 7 @end
// - ImageCell.m
1 #import "ImageCell.h" 2 #define H_Image 120 3 #define W_Image 210 4 @implementation ImageCell 5 6 - (void)awakeFromNib { 7 [super awakeFromNib]; 8 } 9 10 - (void)setSelected:(BOOL)selected animated:(BOOL)animated { 11 [super setSelected:selected animated:animated]; 12 13 // Configure the view for the selected state 14 } 15 16 17 // 重写初始化方法 18 -(id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier{ 19 20 self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]; 21 if (self) { 22 23 [self addSubview:self.aImageView]; 24 [self addSubview:self.numberLabel]; 25 } 26 return self; 27 } 28 29 30 // 懒加载:创建 UI 31 - (UIImageView *)aImageView { 32 33 34 if (_aImageView == nil) { 35 _aImageView = [[UIImageView alloc] initWithFrame:CGRectMake(0.5*self.frame.size.width - 0.5*W_Image, 100-0.5*H_Image, W_Image, H_Image)]; 36 } 37 return _aImageView; 38 } 39 40 - (UILabel *)numberLabel { 41 42 if (_numberLabel == nil) { 43 _numberLabel = [[UILabel alloc] initWithFrame:CGRectMake(5, 5, 100, 20)]; 44 } 45 return _numberLabel; 46 } 47 48 @end
// - RootViewController.m
1 #import "RootViewController.h" 2 #import "ImageCell.h" 3 #import "UIImageView+WebCache.h" 4 @interface RootViewController ()<UITableViewDelegate,UITableViewDataSource> 5 @property(nonatomic,strong)UITableView *tableView; 6 @property(nonatomic,strong)NSArray *imageArray;// 存储图片地址 7 @end 8 9 @implementation RootViewController 10 11 - (void)viewDidLoad { 12 [super viewDidLoad]; 13 self.view.backgroundColor = [UIColor redColor]; 14 15 // tableView 16 [self.view addSubview:self.tableView]; 17 18 // 网址(数组) 19 [self ImageArrayWithUrl]; 20 } 21 22 // 创建 tableView 23 - (UITableView *)tableView { 24 if (_tableView == nil) { 25 _tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 30, self.view.frame.size.width, self.view.frame.size.height) style:UITableViewStylePlain]; 26 _tableView.delegate = self; 27 _tableView.dataSource = self; 28 } 29 return _tableView; 30 } 31 32 // 路径 33 - (void)ImageArrayWithUrl{ 34 35 self.imageArray = [NSArray arrayWithObjects: 36 @"https://pic.ikafan.com/imgp/L3Byb3h5L2h0dHAvaW1hZ2UxMDMuMzYwZG9jLmNvbS9Eb3dubG9hZEltZy8yMDE3LzAxLzI4MDIvOTAyMDM0ODhfNg==.jpg", 37 @"https://b-ssl.duitang.com/uploads/item/201205/09/20120509000808_dfEaA.thumb.700_0.jpeg", 38 @"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1521633999711&di=2ad8ffc560ee31209cf631620ba1b22a&imgtype=0&src=http%3A%2F%2Fimg4q.duitang.com%2Fuploads%2Fitem%2F201112%2F26%2F20111226130836_zw3YS.jpg",nil]; 39 } 40 41 // 行数 42 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 43 44 return (int)self.imageArray.count; 45 } 46 47 // 行高 48 -(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{ 49 50 return 200; 51 } 52 53 // 创建单元格 54 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 55 56 static NSString *cellIndentifier = @"cell"; 57 ImageCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIndentifier]; 58 if (cell == nil) { 59 cell = [[ImageCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIndentifier]; 60 } 61 62 cell.numberLabel.text = [NSString stringWithFormat:@"Num.%ld",indexPath.row + 1]; 63 // 从网上加载图片 64 65 // // 方式一 66 // [cell.aImageView setImageWithURL:[NSURL URLWithString:[_imageArray objectAtIndex:indexPath.row]]]; 67 68 69 // // 方式二:除了带 options 选项的方法,其他的方法都是综合存储,也就是内存缓存和磁盘缓存结合的方式 70 // // 如果你只需要内存缓存,那么在 options 这里选择 SDWebImageCacheMemoryOnly 71 // [cell.aImageView setImageWithURL:[NSURL URLWithString:[_imageArray objectAtIndex:indexPath.row]] placeholderImage:[UIImage imageNamed:@"002.jpg"] options:SDWebImageRefreshCached]; 72 73 74 // // 方式三 75 // [cell.aImageView setImageWithURL:[NSURL URLWithString:[_imageArray objectAtIndex:indexPath.row]] placeholderImage:[UIImage imageNamed:@"001.jpeg"]]; 76 77 78 // // 方式四 79 // [cell.aImageView setImageWithURL:[NSURL URLWithString:[_imageArray objectAtIndex:indexPath.row]] completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType) { 80 // NSLog(@"Hepburn so beauty."); 81 // }]; 82 83 84 // 方式五 85 [cell.aImageView setImageWithURL:[NSURL URLWithString:[_imageArray objectAtIndex:indexPath.row]] placeholderImage:[UIImage imageNamed:@"002.jpg"] completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType) { 86 NSLog(@"Hepburn so beauty."); 87 }]; 88 89 90 91 // // 方式六:加载图片 92 // SDWebImageManager *manager = [SDWebImageManager sharedManager]; 93 // [manager downloadWithURL:[NSURL URLWithString:[self.imageArray objectAtIndex:indexPath.row]] options:SDWebImageCacheMemoryOnly progress:^(NSInteger receivedSize, NSInteger expectedSize) { 94 // 95 // NSLog(@"----...----....----"); 96 // } completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished) { 97 // 98 // cell.aImageView.image = image; 99 // NSLog(@"Hepburn so beauty."); 100 // }]; 101 102 return cell; 103 } 104 105 @end
运行效果:第三张下载失败,显示本地图片
链接:SDWebImage
https://pan.baidu.com/s/1JSCTDy8SMY5tpee2AXR4QA
8s8l
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)