SDWebImage(4.0)

流程(以UIImageView+WebCache为例)

UIImage+WebCache

这个分类只是提供了设置图片的一些方法,真正的实现是在UIView+WebCache.
由于SDWebImage也提供UIButton+WebCache,所以将所有的实现都放在了UIView这个父类中来实现.
UIImage+WebCache中提供了:

- (void)sd_setImageWithURL:(nullable NSURL *)url;
- (void)sd_setImageWithURL:(nullable NSURL *)url placeholderImage:(nullable UIImage *)placeholder;
- (void)sd_setImageWithURL:(nullable NSURL *)url placeholderImage:(nullable UIImage *)placeholder options:(SDWebImageOptions)options;
- (void)sd_setImageWithURL:(nullable NSURL *)url completed:(nullable SDExternalCompletionBlock)completedBlock;
- (void)sd_setImageWithURL:(nullable NSURL *)url placeholderImage:(nullable UIImage *)placeholder completed:(nullable SDExternalCompletionBlock)completedBlock;
- (void)sd_setImageWithURL:(nullable NSURL *)url placeholderImage:(nullable UIImage *)placeholder options:(SDWebImageOptions)options completed:(nullabel SDExternalCompletionBlock)completedBlock;
- (void)sd_setImageWithURL:(nullable NSURL *)url placeholderimage:(nullable UIImage *)placeholder options:(SDWebImageOptions)options progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock completed:(nullable SDExternalCompletionBlock)completedBlock;

url: 图片url
placeholder: 占位图片
options: 策略, 枚举值:

//默认情况下,如果一个url在下载的时候失败了,那么这个url会被加入黑名单并且library不会尝试再次下载,这个flag会阻止library把失败的url加入黑名单(简单来说如果选择了这个flag,那么即使某个url下载失败了,sdwebimage还是会尝试再次下载他
SDWebImageRetryFailed = 1 << 0,

//默认情况下,图片会在交互发生的时候下载(例如你滑动tableview的时候),这个flag会禁止这个特性,导致的结果就是在scrollview减速的时候,才会开始下载(也就是你滑动的时候scrollview不下载,你手从屏幕上移走,scrollview开始减速的时候才会开始下载图片
SDWebImageLowPriority = 1 << 1,

//这个flag禁止磁盘缓存,只有内存缓存
SDWebImageCacheMemoryOnly = 1 << 2,

//这个flag会在图片下载的时候就显示(就像你用浏览器浏览网页的时候那种图片下载,一截一截的显示(待确认))
SDWebImageProgressiveDownload = 1 << 3,

//一个图片即使已经缓存了,还是会根据url进行重新请求.并且缓存策略依据NSURLCache而不是SDWebImage,
SDWebImageRefreshCached = 1 << 4,

//启动后台下载,加入你进入一个页面,有一张图片正在下载这时候你让app进入后台,图片还是会继续下载(这个估计要开backgroundfetch才有用)
SDWebImageContinueInBackground = 1 << 5,

//可以控制存在NSHTTPCookieStore的cookies.
SDWebImageHandleCookies = 1 << 6,

//允许不安全的SSL证书,在正式环境中慎用
SDWebImageAllowInvalidSSLCertificates = 1 << 7,

//默认情况下,image在装载的时候是按照他们在队列中的顺序装载的(就是先进先出).这个flag会把他们移动到队列的前端,并且立刻装载,而不是等到当前队列装载的时候再装载.
SDWebImageHighPriority = 1 << 8,
    
//默认情况下,占位图会在图片下载的时候显示.这个flag开启会延迟占位图显示的时间,等到图片下载完成之后才会显示占位图.
SDWebImageDelayPlaceholder = 1 << 9,

//是否transform图片
SDWebImageTransformAnimatedImage = 1 << 10,
    
//默认情况下,图片是在下载完成后加载到图片视图。但是在一些情况下,我们想要在设置图片之前进行图片处理(比如,提供一个过滤或添加一个折叠动画)。使用这个标志,如果你想在下载成功后在完成块中手动设置图片。
SDWebImageAvoidAutoSetImage = 1 << 11,
    
//默认情况下,图片解码为原始的大小。在iOS,这个标志会把图片缩小到与设备的受限内容相兼容的大小。如果设置了SDWebImageProgressDownload标志,那么缩小被设置为无效。
SDWebImageScaleDownLargeImages = 1 << 12,
    
//默认情况下,当图像缓存在内存中时,我们不会查询磁盘数据。这个flag可以强制同时查询磁盘数据。此标志建议与“SDWebImageQueryDiskSync”一起使用,以确保图像在相同的runloop中加载
SDWebImageQueryDataWhenInMemory = 1 << 13,
    
//默认情况下,我们同步查询内存缓存,异步查询磁盘缓存。这个flag可以强制同步查询磁盘缓存,以确保在同一个runloop中加载图片。如果禁用内存缓存或在其他情况下禁用内存缓存,此flag可以避免在cell重用期间。
SDWebImageQueryDiskSync = 1 << 14,
    
//默认情况下,当缓存丢失时,图像将从网络下载。此flag仅可防止网络从缓存加载。
SDWebImageFromCacheOnly = 1 << 15,

//默认情况下,当您在图像加载完成后使用' SDWebImageTransition '来做一些视图转换时,这个转换只适用于从网络下载图像。这个flag还可以强制为内存和磁盘缓存应用视图转换。
SDWebImageForceTransition = 1 << 16

progressBlock: 下载进度回调
completedBlock: 完成回调

UIView+WebCache

这些方法最终都会走到UIView+WebCache中的方法:

- (void)sd_internalSetImageWithURL:(nullable NSURL *)url placeholderImage:(nullable UIImage *)placeholder options:(SDWebImageOptions)options operationKey:(nullable NSString *)operationKey setImageBlock:(nullable SDSetImageBlock)setImageBlock progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock completed:(nullable SDExternalCompletionBlock)completedBlock context:(nullable NSDictionary<NSString *, id> *)context;

operationKey: 要用作operationkey的字符串。如果为nil,将使用类名

NSString *validOperationKey = operationKey ?: NSStringFromClass([self class]);

setImageBlock: block 用于自定义集图像代码
context: 带有额外信息以执行指定更改或流程的内容.

这个方法才是真正视图层执行的方法.

首先判断拿到operationKey,取消这个对应的operation

[self sd_cancelImageLoadOperationWithKey:validOperationKey];

UIView+WebCacheOperation

这个方法存在于UIView+WebCacheOperation中,从存储中拿到对应的operation,如果遵循SDWebImageOperation协议,就先cancel,然后再从存储中移除.
这个分类中用到了NSMapTable(NSMapTable(后续添加)).
同时还用到了@synchronized((后续添加))和关联对象(关联对象(后续添加)).
通过关联对象创建NSMapTable类型的关联对象loadOperationKey(加锁操作),用于存储operation.
在这分类中,存取移除都进行了加锁操作.
存储的operation都是遵循SDWebImageOperation协议的,这个协议中只有取消操作.

然后创建imageURLKey关联对象.

下一步如果设置了占位图并且策略也设置了占位图,进行设置占位图(主线程更新UI).

if (!(options & SDWebImageDelayPlaceholder)) {
    dispatch_main_async_safe(^{
         self sd_setImage:placeholder imageData:nil basedOnClassOrViaCustomSetImageBlock:setImageBlock];
    });
}

再下一步判断url,如果为nil,直接返回错误信息,不为nil则继续进行下一步.

重置下载进度.

// reset the progress
self.sd_imageProgress.totalUnitCount = 0;
self.sd_imageProgress.completedUnitCount = 0;

获取SDWebImageManager单例对象, 这就进入了管理类

SDWebImageManager

首先设置进度管理block.

__weak __typeof(self)wself = self;
SDWebImageDownloaderProgressBlock combinedProgressBlock = ^(NSInteger receivedSize, NSInteger expectedSize, NSURL * _Nullable targetURL) {
    wself.sd_imageProgress.totalUnitCount = expectedSize;
    wself.sd_imageProgress.completedUnitCount = receivedSize;
    if (progressBlock) {
        progressBlock(receivedSize, expectedSize, targetURL);
    }
};

manager进行下载操作.

- (id <SDWebImageOperation>)loadImageWithURL:(nullable NSURL *)url options:(SDWebImageOptions)options progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock completed:(nullable SDExternalCompletionBlock)completedBlock;

这个方法中,首先进行SDWebImageCombinedOperation这个operation的创建,设置manager.创建SDWebImageManagerSDWebImageCombinedOperation的依赖,其中SDWebImageCombinedOperation设置了SDWebImageOperation的代理,实现cancel方法.

SDWebImageCombinedOperation *operation = [SDWebImageCombinedOperation new];
operation.manager = self;

判断是否是错误url. SDWebImageManager持有一个NSMutableSet类型的错误对象,用于保存错误url.

BOOL isFailedUrl = NO;
if (url) {
    LOCK(self.failedURLsLock);
    isFailedUrl = [self.failedURLs containsObject:url];
    UNLOCK(self.failedURLsLock);
}

将这个SDWebImageCombinedOperation这个对象加入到manager持有的runningOperations中.

LOCK(self.runningOperationsLock);
[self.runningOperations addObject:operation];
UNLOCK(self.runningOperationsLock);

获取缓存策略SDImageCacheOptions, 这是一个枚举值:

//默认情况下,当图片缓存在内存中时,我们不会查询磁盘数据。这个mask可以强制同时查询磁盘数据
SDImageCacheQueryDataWhenInMemory = 1 << 0,

//默认情况下,我们同步查询内存缓存,异步查询磁盘缓存。这个mask可以强制同步查询磁盘缓存
SDImageCacheQueryDiskSync = 1 << 1,

//默认情况下,图像会根据原始大小进行解码。在iOS系统中,这个flag会将图像缩小到与设备有限的内存兼容的大小
SDImageCacheScaleDownLargeImages = 1 << 2
SDImageCacheOptions cacheOptions = 0;
if (options & SDWebImageQueryDataWhenInMemory) cacheOptions |= SDImageCacheQueryDataWhenInMemory;
if (options & SDWebImageQueryDiskSync) cacheOptions |= SDImageCacheQueryDiskSync;
if (options & SDWebImageScaleDownLargeImages) cacheOptions |= SDImageCacheScaleDownLargeImages;

SDImageCache

再接下来就进入到SDImageCache这个类中,这是负责缓存的类.

- (nullable NSOperation *)queryCacheOperationForKey:(nullable NSString *)key options:(SDImageCacheOptions)options done:(nullable SDCacheQueryCompletedBlock)doneBlock;

默认情况下keyurl.

首先通过key在内存中寻找是否有图片缓存.
SDImageCache持有SDMemoryCache类型的memCache, 用于内存缓存.SDMemoryCache继承自NSCache(NSCache(后续添加)).

UIImage *image = [self imageFromMemoryCacheForKey:key];

如果图片存在,并且策略也是,那就直接doneBlock回调.

如果没有内存缓存, 创建异步操作,进行磁盘查询.在磁盘通过keyMD5加密,进行获取NSData文件,进行解码,获取UIImage对象.同时将UIImage对象放入内存缓存中.

NSData *diskData = [self diskImageDataBySearchingAllPathsForKey:key];
UIImage *diskImage;
SDImageCacheType cacheType = SDImageCacheTypeDisk;
if (image) {
    // the image is from in-memory cache
    diskImage = image;
    cacheType = SDImageCacheTypeMemory;
} else if (diskData) {
    // decode image data only if in-memory cache missed
    diskImage = [self diskImageForKey:key data:diskData options:options];
    if (diskImage && self.config.shouldCacheImagesInMemory) {
        NSUInteger cost = SDCacheCostForImage(diskImage);
        [self.memCache setObject:diskImage forKey:key cost:cost];
    }
}

最后将操作返回赋值给SDWebImageCombinedOperationcacheOperation.

SDWebImageDownloader

如果缓存没有就进入SDWebImageDownloader这个类中, 进行图片下载操作

- (nullable SDWebImageDownloadToken *)downloadImageWithURL:(nullable NSURL *)url options:(SDWebImageDownloaderOptions)options progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock completed:(nullable SDWebImageDownloaderCompletedBlock)completedBlock;

此方法中进行图片下载.

下载完成之后,进行缓存操作.
如果是错误url,加入错误列表中.
如果下载成功,进行内存缓存,然后将图片进行编码成NSData文件,存储到磁盘.

最后进行图片显示.

这是SDWebImage的一个大致流程.

一些问题

  1. SDWebImage图片类型识别问题
    NSData+ImageContentType中,根据文件头可以获取文件类型.

    case 0xFF:
        return SDImageFormatJPEG;
    case 0x89:
        return SDImageFormatPNG;
    case 0x47:
        return SDImageFormatGIF;
    case 0x49:
    case 0x4D:
        return SDImageFormatTIFF;
    case 0x52: {
        if (data.length >= 12) {
            //RIFF....WEBP
            NSString *testString = [[NSString alloc] initWithData:[data subdataWithRange:NSMakeRange(0, 12)] encoding:NSASCIIStringEncoding];
            if ([testString hasPrefix:@"RIFF"] && [testString hasSuffix:@"WEBP"]) {
                return SDImageFormatWebP;
            }
        }
        break;
    }
    case 0x00: {
        if (data.length >= 12) {
            //....ftypheic ....ftypheix ....ftyphevc ....ftyphevx
            NSString *testString = [[NSString alloc] initWithData:[data subdataWithRange:NSMakeRange(4, 8)] encoding:NSASCIIStringEncoding];
            if ([testString isEqualToString:@"ftypheic"]
                || [testString isEqualToString:@"ftypheix"]
                || [testString isEqualToString:@"ftyphevc"]
                || [testString isEqualToString:@"ftyphevx"]) {
                return SDImageFormatHEIC;
            }
            if ([testString isEqualToString:@"ftypmif1"] || [testString isEqualToString:@"ftypmsf1"]) {
                return SDImageFormatHEIF;
            }
        }
        break;
    }
    

    图片类型识别(后续添加)

  2. 异步加载多张图片时,SDWebImage是怎么做的? 队列执行方式?超时操作?
    SDWebImageDownloader类持有多个共有属性:

    //最大并发数, 默认为6
    @property (assign, nonatomic) NSInteger maxConcurrentDownloads;
    
    //任务超时时长, 默认15s
    @property (assign, nonatomic) NSTimeInterval downloadTimeout;
    
    //队列执行方式, 默认先进先出
    @property (assign, nonatomic) SDWebImageDownloaderExecutionOrder executionOrder;
    
  3. 图片为什么用MD5编码的URL作为文件名?
    MD5算法具有以下特点:
    1、压缩性:任意长度的数据,算出的MD5值长度都是固定的。
    2、容易计算:从原数据计算出MD5值很容易。
    3、抗修改性:对原数据进行任何改动,哪怕只修改1个字节,所得到的MD5值都有很大区别。
    4、强抗碰撞:已知原数据和其MD5值,想找到一个具有相同MD5值的数据(即伪造数据)是非常困难的。

  4. 缓存时长是多少?

    static const NSInteger kDefaultCacheMaxCacheAge = 60 * 60 * 24 * 7; // 1 week
    
  5. 清除特定图片缓存?

    1. 使用options:SDWebImageRefreshCached刷新缓存,但是反应该方法有闪烁问题,甚至有时并没有更新图片,所以保险起见,最好还是手动清缓存的方式。
    2. 每次清除掉图片缓存,重新加载的方式:
    	NSURL *imageURL = [NSURL URLWithString:@"http://upload-images.jianshu.io/upload_images/949086-5d2c51f1e3a9cddd.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/999"];
    // 获取对应URL链接的key
        NSString *key = [[SDWebImageManager sharedManager] cacheKeyForURL:imageURL];
        NSString *pathStr = [[SDImageCache sharedImageCache] defaultCachePathForKey:key];
        NSLog(@"key存储的路径: %@", pathStr);
    // 删除对应key的文件
        [[SDImageCache sharedImageCache] removeImageForKey:key withCompletion:^{
            [self.tempImageView sd_setImageWithURL:imageURL placeholderImage:[UIImage imageNamed:@"placeholderHead.png"]];
        }];
    
  6. 清除过期文件的时机
    清除过期旧文件的时间点有两处:程序切到后台、杀死APP时。

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(deleteOldFiles) name:UIApplicationWillTerminateNotification object:nil];
    
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(backgroundDeleteOldFiles) name:UIApplicationDidEnterBackgroundNotification object:nil];
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值