解读SDWebImage

2018-11-30  本文已影响0人  劳模007_Mars
SDWebImage_logo.png

SDWebImage是一个功能强大的图片缓存框架,可以实现网络图片加载和缓存。那么SDWebImage的缓存机制是如何实现的呢?缓存时间是多久?清除缓存的策略是怎样的?让我们带着这些问题,走进源码,解读SDWebImage。

SDWebImage提供一个UIImageView的分类 UIImageView+WebCache,用来加载和缓存来通关HTTP传输的图片资源。提供了缓存管理、异步下载、缓存校验等功能。

缓存机制

SDWebImage 的图片缓存采用的是 MemoryDisk 双重Cache机制,也就是二级缓存。SDWebImage的缓存机制可以简单概括为:第一次请求加载图片后将图片缓存在内存中,并通过md5加密将图片的url进行加密处理,加密处理后的值作为key,将图片存储在磁盘中。当再次加载图片时,先在内存缓存中寻找,如果内存中找不到图片,则在默认的磁盘中寻找,如果还是找不到,则再请求加载图片

SDWebImage流程.png

核心类:

SDWebImageManager
SDImageCache
SDWebImageDownloader
SDWebImageDownloadOperation

缓存方法

下面通过一张框架图来解释SDWebImage的框架结构:


框架.png

最上层的UIImageView+WebCache这个类里面提供了一下几个方法来供我们进行网络图片加载等操作:
1. sd_setImageWithURL:

//图片缓存的基本代码
[self.imageView sd_setImageWithURL:imagePath];

2. sd_setImageWithURL: completed:

//用block 可以在图片加载完成之后做些事情
[self.imageView sd_setImageWithURL:imagePath completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) 
{
    NSLog(@"图片加载完成之后可以在这里做些事情");
}];

3. sd_setImageWithURL: placeholderImage:

//给一张默认图片,先使用默认图片,当图片加载完成后再替换
[self.imageView sd_setImageWithURL:imagePath placeholderImage:[UIImage imageNamed:@"default"]];

4. sd_setImageWithURL: placeholderImage: completed:

//使用默认图片,而且用block 在完成后做一些事情
[self.imageView sd_setImageWithURL:imagePath placeholderImage:[UIImage imageNamed:@"default"] completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL)
{
    NSLog(@"图片加载完成后做的事情");
}];

5. sd_setImageWithURL: placeholderImage: options:

//options 选择方式
[self.imageView sd_setImageWithURL:imagePath placeholderImage:[UIImage imageNamed:@"default"] options:SDWebImageRetryFailed];

其中,除了方法5,其他的方法都是综合存储,也就是内存缓存和磁盘缓存结合的方式。方法5增加了options用来满足不同需求的缓存方式。

下面我们看一下SDWebImage为我们提供的options:

    /**
     * 默认情况下,当URL未能下载时,URL将被列入黑名单,因此库将不会继续尝试。
     * 此标志禁用此黑名单。*
     */
    SDWebImageRetryFailed = 1 << 0,

    /**
     * 默认情况下,在UI交互期间启动图像下载,此标志禁用此功能,
     * 例如,导致UIScrollView减速下载延迟。
     */
    SDWebImageLowPriority = 1 << 1,

    /**
     * 下载完成后,此标志禁用磁盘缓存,仅缓存在内存中
     */
    SDWebImageCacheMemoryOnly = 1 << 2,

    /**
     * 此标志启用渐进式下载,图像在下载过程中逐步显示,就像浏览器一样。
     * 默认情况下,图像只显示一次完全下载。
     */
    SDWebImageProgressiveDownload = 1 << 3,

    /**
     * 刷新缓存
     */
    SDWebImageRefreshCached = 1 << 4,

    /**
     * 后台加载
     */
    SDWebImageContinueInBackground = 1 << 5,

    /**
     * 通过设置处理存储在NSHTTPCookieStorage中的cookie
     * NSMutableURLRequest.HTTPShouldHandleCookies = YES;
     */
    SDWebImageHandleCookies = 1 << 6,

    /**
     * 允许使用无效的SSL证书
     */
    SDWebImageAllowInvalidSSLCertificates = 1 << 7,

    /**
     * 优先下载
     */
    SDWebImageHighPriority = 1 << 8,
    
    /**
     * 延迟占位符
     */
    SDWebImageDelayPlaceholder = 1 << 9,

    /**
     * 改变动画形象
     */
    SDWebImageTransformAnimatedImage = 1 << 10,
    
    /**
     * 默认情况下,下载后会将图像添加到imageView。 但在某些情况下,我们想要
     *在设置图像之前握手(例如应用滤镜或添加交叉渐变动画)
     *如果要在成功时手动设置完成图像,请使用此标志
     */
    SDWebImageAvoidAutoSetImage = 1 << 11,
    
    /**
      * 默认情况下,图像会根据其原始大小进行解码。 在iOS上,此标志将缩小
      *图像尺寸与设备的受限内存兼容。
      *如果设置了“SDWebImageProgressiveDownload”标志,则停用缩小比例。
     */
    SDWebImageScaleDownLargeImages = 1 << 12,
    
    /**
     * 默认情况下,当图像缓存在内存中时,我们不查询磁盘数据。 此掩码可以强制同时查询磁盘数据。
     *建议将此标志与`SDWebImageQueryDiskSync`一起使用,以确保图像在同一个runloop中加载。
     */
    SDWebImageQueryDataWhenInMemory = 1 << 13,
    
    /**
     * 默认情况下,我们同步查询内存缓存,异步查询磁盘缓存。 此掩码可以强制同步查询磁盘缓存,以确保在同一个runloop中加载映像。
     *如果禁用内存缓存或在某些其他情况下,此标志可以避免在单元重用期间闪烁。
     */
    SDWebImageQueryDiskSync = 1 << 14,
    
    /**
     * 默认情况下,当缓存丢失时,将从网络下载映像。 此标志可以阻止网络仅从缓存加载。
     */
    SDWebImageFromCacheOnly = 1 << 15,
    /**
     * 默认情况下,当您使用`SDWebImageTransition`在图像加载完成后进行某些视图转换时,此转换仅适用于从网络下载图像。 此掩码也可以强制为内存和磁盘缓存应用视图转换。
     */
    SDWebImageForceTransition = 1 << 16

==延伸==
“<<”是位运算中的左移运算符,第一个枚举值SDWebImageRetryFailed = 1 << 0,十进制1转化为二进制:0b00000001,这里<<0将所有二进制位左移0位,那么还是0b00000001,最终SDWebImageRetryFailed 值为1.

第二个枚举值SDWebImageLowPriority =1<<1,这里是将1的二进制所有位向左移动1位,空缺的用0补齐,那么0b00000001变成0b00000010,十进制为2则SDWebImageLowPriority值为2。

缓存时间

在SDImageCachaConfig.m文件里面,SDWebImage设置了一些默认值,其中就包括最大存储时间,一周:

static const NSInteger kDefaultCacheMaxCacheAge = 60 * 60 * 24 * 7; // 1 week

判断图片类型

根据imageData的第一个字节,可以判断其图片类型。

第一个字节 图片类型
0xFF jpeg
0x89 png
0x47 gif
0x4D\0x49 tiff
0x52 将imageData的前12个字节转化为字符串,如果是RIFF前缀和WEBP后缀,则图片类型是webp
0x00 将imageData的前12个字节转化为字符串,如果是ftypheic 、ftypheix、ftyphevc 、ftyphevx后缀,则是HEIC或者HEIF类型

图片缓存清理

当系统发出内存不足通知时,会将内存中的所有图片缓存都删除掉;
当程序进入后台时,会对磁盘的文件数据进行清理;
当收到程序关闭通知时,会对磁盘中的文件数据进行清理。

SDWebImage 会在每次 APP 结束的时候执行清理任务。 清理缓存的规则分两步进行。 第一步先清除掉过期的缓存文件。 如果清除掉过期的缓存之后,空间还不够。 那么就继续按文件时间从早到晚排序,先清除最早的缓存文件,直到剩余空间达到要求。

具体点,SDWebImage 是怎么控制哪些缓存过期,以及剩余空间多少才够呢? 通过两个属性:

@interface SDImageCache : NSObject

@property (assign, nonatomic) NSInteger maxCacheAge;
@property (assign, nonatomic) NSUInteger maxCacheSize;

maxCacheAge 是文件缓存的时长, SDWebImage 会注册两个通知:

[[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(cleanDisk)
                                                     name:UIApplicationWillTerminateNotification
                                                   object:nil];

[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(backgroundCleanDisk)
                                             name:UIApplicationDidEnterBackgroundNotification
                                           object:nil];

分别在应用进入后台和结束的时候,遍历所有的缓存文件,如果缓存文件超过 maxCacheAge 中指定的时长,就会被删除掉。

同样的, maxCacheSize 控制 SDImageCache 所允许的最大缓存空间。 如果清理完过期文件后缓存空间依然没达到 maxCacheSize 的要求, 那么就会继续清理旧文件,直到缓存空间达到要求为止。

磁盘缓存清理步骤

1.获取磁盘中图片的最后修改日期。(为了减少磁盘和内存数据交换,读取是并不将整个文件读入内存,仅仅将文件的一些属性读入内存中,包括最后修改日期,该文件是否为文件夹,文件的大小和对应文件的文件路径)
2.根据最后修改日期将图片进行分类,将那些已经存放超过最长存放时间的文件存储在删除数组,其他的文件信息存储在另一个字典中。并计算除去要删除的文件之外的所有文件大小
3.根据删除数组中的文件路径,将对应的文件删除。
4.判断剩下的文件大小是否超过用户现在的磁盘最大容量。
5.如果超过,则将剩余的文件进行安修改时间进行升序排列,然后删除修改时间最早的文件,直到甚剩余文件大小小于最大磁盘容量的一半。

结语

至此就SDWebImage图片缓存机制和清除缓存做了一个简单的介绍,SDWebImage 是一个功能强大的开源库,而且一直保持着更新。功能请打但并不算很复杂,仔细研读一下它的代码,会发现内部很多机制设计的非常巧妙。篇幅有限,介绍就此结束,想要熟练的运用SDWebImage,还需要自己取研究源码,以及在实践中多多运用。

上一篇下一篇

猜你喜欢

热点阅读