iOS三方框架之 – SDWebImage解析
通过 SDWebImageManager 来进行管理,主要模块有三个:加载模块、缓存模块、下载模块。

SDWebImage加载图片的流程
先放一张加载流程图:

从
UIView+WebCache.m中的sd_internalSetImageWithURL:placeholderImage:options:operationKey:setImageBlock:progress:completed
开始分析
- 判断当前view有没有任务进行,每一个view只能对应一个任务,有一个全局的
typedef NSMutableDictionary SDOperationsDictionary;
用来存储着每一个view对应的下载operation
NSString *validOperationKey = operationKey ?: NSStringFromClass([self class]); [self sd_cancelImageLoadOperationWithKey:validOperationKey]; objc_setAssociatedObject(self, &imageURLKey, url, OBJC_ASSOCIATION_RETAIN_NONATOMIC); //取消对应的Operation - (void)sd_cancelImageLoadOperationWithKey:(nullable NSString *)key { // Cancel in progress downloader from queue SDOperationsDictionary *operationDictionary = [self operationDictionary]; id operations = operationDictionary[key]; if (operations) { if ([operations isKindOfClass:[NSArray class]]) { for (id operation in operations) { if (operation) { [operation cancel]; } } } else if ([operations conformsToProtocol:@protocol(SDWebImageOperation)]){ [(id) operations cancel]; } [operationDictionary removeObjectForKey:key]; } } 复制代码
- 开始调用loadImageWithUrl:
id operation = [SDWebImageManager.sharedManager loadImageWithURL:url options:options progress:progressBlock completed:^(UIImage *image, NSData *data, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) { __strong __typeof (wself) sself = wself; [sself sd_removeActivityIndicator]; if (!sself) { return; } dispatch_main_async_safe(^{ if (!sself) { return; } if (image && (options & SDWebImageAvoidAutoSetImage) && completedBlock) { completedBlock(image, error, cacheType, url); return; } else if (image) { [sself sd_setImage:image imageData:data basedOnClassOrViaCustomSetImageBlock:setImageBlock]; //下一个runloop刷新周期对当前view进行刷新 [sself sd_setNeedsLayout]; } else { if ((options & SDWebImageDelayPlaceholder)) { [sself sd_setImage:placeholder imageData:nil basedOnClassOrViaCustomSetImageBlock:setImageBlock]; [sself sd_setNeedsLayout]; } } if (completedBlock && finished) { completedBlock(image, error, cacheType, url); } }); }]; 复制代码
这一步就会根据上面那张流程图那样:先去查找缓存——>再去进行下载——>分两步(UI显示和图片缓存),这里UI显示的时候会根据 SDWebImageOptions 来决定是否需要对图片进行一些处理,比如:
/** * 只进行内存缓存,不进行磁盘缓存 */ SDWebImageCacheMemoryOnly = 1 << 2, /** * 这个标志可以渐进式下载,显示的图像是逐步在下载(就像你用浏览器浏览网页的时候那种图片下载,一截一截的显示 */ SDWebImageProgressiveDownload = 1 << 3, /** * 在加载图片时加载占位图。 此标志将延迟加载占位符图像,直到图像完成加载。 */ SDWebImageDelayPlaceholder = 1 << 9, /** * 图片在下载后被加载到imageView。但是在一些情况下,我们想要设置一下图片(引用一个滤镜或者加入透入动画) * 使用这个来手动的设置图片在下载图片成功后 */ SDWebImageAvoidAutoSetImage = 1 << 11, 复制代码
等等,这里没有列举完全,可以自己看源码枚举里的注释。
SDWebImage缓存模块
SDWebImage使用的是内存和磁盘双缓存。两个类: SDImageCacheConfig:对当前缓存的配置 和 SDImageCache:具体的缓存查找
SDImageCacheConfig中有一些参数,比如:
是否要压缩图片,默认YES: @property (assign, nonatomic) BOOL shouldDecompressImages; 压缩图片可以提高性能,但会消耗内存
是否需要内存缓存,默认YES @property (assign, nonatomic) BOOL shouldCacheImagesInMemory;
还有一些属性也不一一列举了,可以自行查看源码。
SDImageCache里包含了两个属性:
//内存缓存 @property (strong, nonatomic, nonnull) NSCache *memCache; //磁盘缓存 @property (strong, nonatomic, nonnull) NSString *diskCachePath; 复制代码
- 内存缓存 SDWebImage提供了一个继承于 NSCache 的缓存类 AutoPurgeCache ,这个类注册了一个内存警告的通知:
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(removeAllObjects) name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
在收到内存警告的时候,进行缓存清理。
- 磁盘缓存
- 生成一个文件目录:
- (nullable NSString *)makeDiskCachePath:(nonnull NSString*)fullNamespace { NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES); return [paths[0] stringByAppendingPathComponent:fullNamespace]; } 复制代码
- SDWebImage为每一个缓存文件生成了一个md5的文件名,放到文件目录当中。
具体读取图片时的源码:
- (nullable NSOperation *)queryCacheOperationForKey:(nullable NSString *)key done:(nullable SDCacheQueryCompletedBlock)doneBlock { if (!key) { if (doneBlock) { doneBlock(nil, nil, SDImageCacheTypeNone); } return nil; } // 检查内存缓存 UIImage *image = [self imageFromMemoryCacheForKey:key]; if (image) { NSData *diskData = nil; if ([image isGIF]) { diskData = [self diskImageDataBySearchingAllPathsForKey:key]; } if (doneBlock) { doneBlock(image, diskData, SDImageCacheTypeMemory); } return nil; } NSOperation *operation = [NSOperation new]; dispatch_async(self.ioQueue, ^{ if (operation.isCancelled) { // do not call the completion if cancelled return; } @autoreleasepool { //检查磁盘缓存 NSData *diskData = [self diskImageDataBySearchingAllPathsForKey:key]; UIImage *diskImage = [self diskImageForKey:key]; if (diskImage && self.config.shouldCacheImagesInMemory) { NSUInteger cost = SDCacheCostForImage(diskImage); //磁盘缓存找到后缓存到内存 [self.memCache setObject:diskImage forKey:key cost:cost]; } if (doneBlock) { dispatch_async(dispatch_get_main_queue(), ^{ doneBlock(diskImage, diskData, SDImageCacheTypeDisk); }); } } }); return operation; } 复制代码
SDWebImage下载模块
同样是两个类 SDWebImageDownloader 和 SDWebImageDownloaderOperation SDWebImageDownloader 管理类:负责处理一些公共的信息,比如下载的相关参数、下载队列里的先后顺序、最大下载任务数量、请求头、参数的设置等等
//优先级枚举: typedef NS_ENUM(NSInteger, SDWebImageDownloaderExecutionOrder) { //先进先出 默认 SDWebImageDownloaderFIFOExecutionOrder, //后进先出 SDWebImageDownloaderLIFOExecutionOrder }; //设置优先级 后进先出 if (sself.executionOrder == SDWebImageDownloaderLIFOExecutionOrder) [sself.lastAddedOperation addDependency:operation]; sself.lastAddedOperation = operation; } 复制代码
SDWebImageDownloaderOperation:自定义NSOperation来控制其生命周期
- 重写了start方法去开始下载任务:[self.dataTask resume];
- 下载完成后的回调处理