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

posted on 2018-03-21 16:30  低头捡石頭  阅读(43)  评论(0编辑  收藏  举报

导航