SDWebImage 源码分析

栏目: IOS · 发布时间: 5年前

内容简介:主要围绕三个方面来阐述这是从 github SDWebImage 地址下载的图. 图片说明了整个图片加载的时序.下面根这源码来验证一下上面说的知识 根据 Github 上面提供的整个 SD 的类图可知,主要是

主要围绕三个方面来阐述

  • 加载流程
  • 缓存模块
  • 下载模块

加载流程

SDWebImage 源码分析

这是从 github SDWebImage 地址下载的图. 图片说明了整个图片加载的时序.

双缓存

下面根这源码来验证一下上面说的知识 根据 Github 上面提供的整个 SD 的类图可知,主要是 SDWebImageManager 在操作管理查找流程. 我们就根据大家使用的最多的方法, 来一步步跟踪.

  1. 通常情况下,我们使用这个方法对图片进行加载。
UIImageView+WebCache.h 文件中
-(void)sd_setImageWithURL:(nullable NSURL *)url
          placeholderImage:(nullable UIImage *)placeholder
复制代码
  1. 查找 sd_internalSetImageWithURL 方法.
UIView+WebCache.h 文件中
sd_internalSetImageWithURL ....
复制代码
  1. 如果你设置了 PlaceholderImage, 他会先去显示你的 placeholderImage
if (!(options & SDWebImageDelayPlaceholder)) {
        dispatch_main_async_safe(^{
            [self sd_setImage:placeholder imageData:nil basedOnClassOrViaCustomSetImageBlock:setImageBlock cacheType:SDImageCacheTypeNone imageURL:url];
        });
    }
复制代码
  1. 继续去 SDWebImageManager 中的 loadImageWithURL 查找图片
id <SDWebImageOperation> operation = [manager loadImageWithURL:url options:options context:context progress:combinedProgressBlock completed:^(UIImage *image, NSData *data, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
复制代码
  1. 然后调用 callCacheProcessForOperation 进行缓存查找操作
BOOL shouldQueryCache = (options & SDWebImageFromLoaderOnly) == 0;
if (shouldQueryCache) {
	// 缓存查找
}else {
	// 进行下载操作
}
复制代码
  1. 接上缓存查找过程, 回去 SDImageCache 中的 queryImageForKey 方法继续缓存查找
-(id<SDWebImageOperation>)queryImageForKey:(NSString *)key options:(SDWebImageOptions)options context:(nullable SDWebImageContext *)context completion:(nullable SDImageCacheQueryCompletionBlock)completionBlock {
复制代码
  1. 接下来到 queryCacheOperationForKey 方法

    • 首先去内存中找,如果有就用 Block 的方式传递到上层显示
    // First check the in-memory cache...
    UIImage *image = [self imageFromMemoryCacheForKey:key];
    复制代码
    • 否则就去 Disk 查找
    NSData *diskData = [self diskImageDataBySearchingAllPathsForKey:key];
    复制代码
    • 如果 Disk 有就返回,并且把这个值存入 MemoryCache 中,这样就可以再下次查找中更快的找到对应的图片信息
    [self.memCache setObject:diskImage forKey:key cost:cost];
    复制代码
  2. 如果没找到就去下载图片

callDownloadProcessForOperation
复制代码
  1. 进入下载流程, 生成 SDWebImageDownloadToken 对象.就行下载
[self callStoreCacheProcessForOperation:operation url:url options:options context:context downloadedImage:downloadedImage downloadedData:downloadedData finished:finished progress:progressBlock completed:completedBlock];
复制代码
  1. 下载完成就行存储
[self.imageCache storeImage:transformedImage imageData:cacheData forKey:cacheKey cacheType:storeCacheType completion:nil];
复制代码
* 分别存储到内存和磁盘
```
-(void)storeImage:(nullable UIImage *)image
     imageData:(nullable NSData *)imageData
        forKey:(nullable NSString *)key
      toMemory:(BOOL)toMemory
        toDisk:(BOOL)toDisk
    completion:(nullable SDWebImageNoParamsBlock)completionBlock {
``` 
复制代码
  1. 返回数据显示
[self callCompletionBlockForOperation:operation completion:completedBlock image:transformedImage data:downloadedData error:nil cacheType:SDImageCacheTypeNone finished:finished url:url];
复制代码

到此整个图片加载流程就完了.

SDWebImage 源码分析

缓存模块

下图是最新的 SDWebImage 的文件结构,这里主要看看 Cache 板块

SDWebImage 源码分析
  • 首先介绍这些文件是干啥的(记得之前的 Cache 没这么多文件)
    • SDImageCacheConfig.h SDImageCacheConfig.m 这两个文件是来做一些设置
    • SDMemoryCache.h SDMemoryCache.m 是内存缓存相关的操作
    • SDDiskCache.h SDDiskCache.m 是磁盘缓存相关的操作
    • SDImageCacheDefine.h SDImageCacheDefine.m 主要是为 SDImageCache 定义了一些协议
    • SDImageCachesManager.h SDImageCachesManager.m 主要对 Cache 删除和添加
    • SDImageCache.h SDImageCache.m 操作 SDDiskCacheSDMemoryCache

首先先看设置文件 SDImageCacheConfig

* 定义了默认的磁盘缓存时间, 默认为 1 周 `kDefaultCacheMaxDiskAge = 60 * 60 * 24 *7;`
* shouldDisableiCloud 是否使用 iCloud
* shouldCacheImagesInMemory 是否使用内存缓存
* shouldUseWeakMemoryCache 是否使用弱应用内存缓存
* shouldRemoveExpiredDataWhenEnterBackground 是否删除超过日期的数据
* maxDiskAge 最大磁盘缓存周期为 `1` 周
* maxDiskSize 最大磁盘缓存大小.默认为 0,意味着没有大小限制
* maxMemoryCost 内存缓存大小,默认为 0,意味着没有大小限制
* maxMemoryCount 内存缓存的最大数量,默认 0
* diskCacheExpireType  磁盘缓存失效的类型(用于删除使用)
复制代码

SDMemoryCache

* `SDMemoryCache` 是继承自 系统的 `NSCache`.
* 这个类主要是操作内存,删除,保存的操作
* 首先注册了内存警告通知 `UIApplicationDidReceiveMemoryWarningNotification`
* 保存
	- (void)setObject:(id)obj forKey:(id)key cost:(NSUInteger)g {
		[super setObject:obj forKey:key cost:g];
		if (!self.config.shouldUseWeakMemoryCache) {
    		return;
	    }
	    if (key && obj) {
        // Store weak cache
    		SD_LOCK(self.weakCacheLock);
        [self.weakCache setObject:obj forKey:key];
	      SD_UNLOCK(self.weakCacheLock);
	    }
	}
	
	Note: 这里的保存方法是做了两次保存操作
		1. 使用 [Super setObject:obj forKey:key cost: g] 先给父类设置,也就是给系统的 NSCache 设置缓存
		2. 然后在给当前缓存内的 `NSMapTable` 设置值
		3. 这样也就是拿空间换时间的操作,相当于在内存中是有两份数据的,如果第一份没有,就去拿第二份,这样就保证了快速度的响应. 

* 取值
	-(id)objectForKey:(id)key {
		 id obj = [super objectForKey:key];
	    if (!self.config.shouldUseWeakMemoryCache) {
		      return obj;
	    }
		  if (key && !obj) {
 		   // Check weak cache
 	   	SD_LOCK(self.weakCacheLock);
     	   obj = [self.weakCache objectForKey:key];
   	   SD_UNLOCK(self.weakCacheLock);
   	   if (obj) {
          // Sync cache
          NSUInteger cost = 0;
          if ([obj isKindOfClass:[UIImage class]]) {
              cost = [(UIImage *)obj sd_memoryCost];
          }
          [super setObject:obj forKey:key cost:cost];
      }
	    }
  	  return obj;
	}
	1. 先去 `super` 取值
	2. 如果木有就去 `NSMapTable` 中取值
复制代码

SDDiskCache

* 使用 NSFileManager 管理图片缓存
* 根据 `key` 在它 SDDiskCacheFileNameForKey 方法中,使用 `CC_MD5` 生成一个 新的  `Key`, 转换成 url,存放 data.
* 还有一点就是清理过期的数据,有两种方式
	1. SDImageCacheConfigExpireTypeAccessDate 根据访问时间
	2. SDImageCacheConfigExpireTypeModificationDate 修改时间(默认)
	3. 根据 self.config.maxDiskAge 来对比删除超过时间的图片
	4. 根据 self.config.maxDiskSize 来删除磁盘缓存的数据,清理到self.config.maxDiskSize /2 为止.
复制代码

SDImageCachesManager

* 主要是操作 `SDImageCache` 类,对缓存就行 `存储`, `删除`, `清理`, `查询`
复制代码

SDImageCacheConfig

* 主要是对图片的 `Decode` 操作
复制代码

SDImageCache

* 值得说的是,他在这里监听了两个系统通知
1. UIApplicationWillTerminateNotification
2. UIApplicationDidEnterBackgroundNotification
当这两个方法执行时,其实是做了一件事情,就是清理磁盘缓存 
[self.diskCache removeExpiredData];
复制代码

下载模块

SDWebImageDownloaderConfig

* 设置最大并发数 maxConcurrentDownloads ,默认是 6
* 设置下载超时时间 downloadTimeout ,默认 15s
* 下载执行顺序 executionOrder , 默认先进先出
复制代码

SDWebImageDownloader

Note: 总结就是 使用 系统 NSURLSession 创建 task ,在本类中它实现了 NSURLSessionTaskDelegate, 当数据下载完成,就去显示.
	* 主要是设置下载队列
  	* 拼接 HTTPHeader 
	* 组装 SDWebImageDownloaderOperation 
  	* 设置 Credential
	* 配置 NSOperation 的优先级
	* 返回一个 SDWebImageDownloaderOperation
	* 虽然设置了 NSURLSessionTaskDelegate 的代理在这个类中,但是实际还是把结果交费给了 SDWebImageDownloaderOperation, 统一处理
	
	NSOperation<SDWebImageDownloaderOperation> *dataOperation = [self operationWithTask:dataTask];
    if ([dataOperation respondsToSelector:@selector(URLSession:dataTask:didReceiveResponse:completionHandler:)]) {
    		[dataOperation URLSession:session dataTask:dataTask didReceiveResponse:response completionHandler:completionHandler];
	 } else {
  	  if (completionHandler) {
     	   completionHandler(NSURLSessionResponseAllow);
   		 }
	}
复制代码

SDWebImageDownloaderOperation

Note: 他是集成自 NSOperation, 重写了 Start 方法
	* 在 Start 时,如果 app 进入后台,就取消下载.
	* 设置 NSURLSession 之后就下载任务
	* 其中手动(KVC)出发了 isFinished isExecuting
	* 然后就是在 NSURLSessionTaskDelegate,NSURLSessionDataDelegate,设置显示就回调
复制代码

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

敏捷估计与规划

敏捷估计与规划

[美] Mike Cohn / 宋锐 / 清华大学出版社 / 2007-7 / 39.90元

《敏捷估计与规划》一书为对敏捷项目进行估计与规划提供了权威实际的指导方针。在本书中,敏捷联盟的共同创始人Mike Cohn讨论了敏捷估计与规划的思想,并使用现实的例子与案例分析向您详细地展示了如何完成工作。本书清晰地阐述了有关的概念,并引导读者逐步认识到下列一些问题的答案:我们要构建什么?它的规模有多大?需要在什么时候完成?到那个时候我们到底能完成多少?您首先会认识到优秀的计划由哪些东西组成,接着......一起来看看 《敏捷估计与规划》 这本书的介绍吧!

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换