源码解读-SDWebImage3.8.2

  • 架构


  • 底层处理类:
    SDImageCache(负责image的存储)
    SDWebImageDownloader (负责webImage的下载)
    SDWebImageDownloaderOperation (继承自NSOpration的下载任务类)
  • 工具辅助类
    NSData+ImageContentType(负责根据图片Data获取图片类型)
    SDWebImageCompat(定义根据url后2x,3x处理图片scale(缩放因子)、以及定义安全主队列执行宏)
    UIImage+MultiFormat(根据Data区分类型获取正确的图片)
    UIImage+GIF(处理GIF图片分类)
    UIView+WebCacheOperation(用于存储在View类的operation或者取消operation)
  • 底层之上的处理类
    SDWebImageManager (负责采用SDWebImageDownloader和SDImageCache储存的逻辑整合管理类)
    SDWebImagePrefetcher (封装SDWebImageManager的SDWebImageManager预取器)
    UIButton+WebCache (UIButton设置网络图片的分类)
    UIImageView+HighlightedWebCache (UIImageView设置高亮webImage的分类)
    UIImageView+WebCache(UIImageView 设置webImage的分类)
  • 代码解读


  • SDImageCache

负责图片存储、清理功能,主要分为memoryCache(内存缓存)和diskCache(磁盘缓存)。

故定义存储类型为SDImageCacheType:SDImageCacheTypeNone、SDImageCacheTypeDisk、SDImageCacheTypeMemory。

基本的属性就不在此做篇幅了,解释都很清楚。我们主要记录相关知识点以及设计思路。

此类有意设计为一个单利类。同时也预留出了其他非单利初始化方法,方便用户自己定义一个存储区。通过sharedImageCache生成的是我们全局单利类,采用默认的存储路径;通过- (id)initWithNamespace:(NSString *)ns;- (id)initWithNamespace:(NSString *)ns diskCacheDirectory:(NSString *)directory;两个方法创建自己的存储路径。
下面我们说一下,存、取以及清理。
存的方式我们采用NSCache作为内存读写,采用NSFileManager作为磁盘读写。
关于写:
根据属性是否写入内存:如果写入内存,先[self.memCache setObject:image forKey:key cost:cost];设置NSCache,之后diskCache,diskCache采用GCD异步串行队列存储,异步保证不会阻塞当前线程,串行保证队列所有任务都能保证按FIFO的顺序执行,避免造成线程并发存储造成崩溃。从缓存中取图片的时候,首先会从NSCache中查找,再从DiskCache中查找。
清理缓存:
1. 按照key清理,清除内存缓存(根据shouldCacheImagesInMemory熟悉)->清除disk缓存(异步串行队列执行)
2.全部清除:
全部清除clear
clearMemory([self.memCache removeAllObjects];)、clearDisk(异步串行队列删除缓存地址所有文件->创建缓存路径)
清理clean
清理缓存只有清理disk缓存,先清理过期的图片->按最后修改日期排序直到删除到缓存<最大缓存量(也就是算法中经常说的最近最少使用LRU算法)

  • SDWebImageDownloader

此类也设计为单利,负责从网络上下载图片。网络请求采用NSURLSession处理(当然以前的NSURLConnection已经废弃了)

关于下载流程主要浓缩于一个方法:- (id <SDWebImageOperation>)downloadImageWithURL:(NSURL *)url options:(SDWebImageDownloaderOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageDownloaderCompletedBlock)completedBlock 在这个方法里调用了一个重要的方法- (void)addProgressCallback:(SDWebImageDownloaderProgressBlock)progressBlock completedBlock:(SDWebImageDownloaderCompletedBlock)completedBlock forURL:(NSURL *)url createCallback:(SDWebImageNoParamsBlock)createCallback
这个方法 应用dispatch_barrier_sync(GCD同步栅栏函数并发队列执行任务, 此函数的意义在于同步执行,并且当前任务直到在此之前加进去的所有任务执行完毕才会执行)执行从URLCallbacks取以url为键名的Array(存储了kProgressCallbackKey(进度回调)、kCompletedCallbackKey(完成回调))如果不存在,将其标记为first(第一次下载)并且初始化url对应的数组,然后将添保存进度和完成回调的字典添加进数组。如果第一次下载回调creatCallBack()。接下来我们看creatCallBack回调里做了什么事情:生成NSMutableURLRequest(配置相关属性)->生成SDWebImageDownloaderOperation(继承抽象类NSOperation的自定义类->配置operation的相关属性 -> 添加到自定义并发队列([NSOperationQueue addOperation] 添加完就会调用[NSOperation start])->如果执行顺序是SDWebImageDownloaderLIFOExecutionOrder,添加这次上次op依赖于这次op。那么SDWebImageDownloaderOperation又做了什么呢?
它是继承自NSOperation抽象类的,并且实现了NSURLSessionTaskDelegate、NSURLSessionDataDelegate、SDWebImageOperation(自定义Operation协议)

  • (void)start
    通过可选项SDWebImageDownloaderOptions是否为SDWebImageDownloaderContinueInBackground后台继续下载模式,开启后台激活后台长运行任务[app beginBackgroundTaskWithExpirationHandler:^{}],用已经传进来的session继续执行任务。其中使用同步锁@synchronized (self){}执行属性赋值等操作
  • NSURLSessionDataDelegate
    - (void)URLSession:(NSURLSession *)session
    dataTask:(NSURLSessionDataTask *)dataTask
    didReceiveResponse:(NSURLResponse *)response
    completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler; // 开始接收到response的代理。如果返回statusCode < 400 && statusCode != 304说明返回数据正常,否则说明返回错误。正确赋值给预期大小,初始化imageData数据,回调进度block,通知SDWebImageDownloadReceiveResponseNotification;错误情况下取消任务发送SDWebImageDownloadStopNotification通知,回调完成block返回错误信息。
    - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data;// 开始接收数据代理,拼接接收到的数据,并且根据是否SDWebImageDownloaderProgressiveDownload,是否处理渐进下载。首先如果高度+宽度=0。获取宽度、高度、方向。CGImageSourceCreateWithData()创建CGImageSourceRef,获取CGImageSourceRef的info信息CFDictionaryRef,其中包括高度val = CFDictionaryGetValue(properties, kCGImagePropertyPixelHeight)、宽度CFDictionaryGetValue(properties, kCGImagePropertyPixelWidth)、以及方向CFDictionaryGetValue(properties, kCGImagePropertyOrientation)。

如果当前width+height>0&& 当前数据大小<预期数据大小,根据当前数据生成图片 CGImageSourceCreateImageAtIndex生成CGImageRef, CGBitmapContextCreate()生成位图context,CGContextDrawImage()绘制到当前的位图context中,从当前的context中生成图片CGBitmapContextCreateImage(),根据imageRef生成image。
- (void)URLSession:(NSURLSession *)session
dataTask:(NSURLSessionDataTask *)dataTask
willCacheResponse:(NSCachedURLResponse *)proposedResponse
completionHandler:(void (^)(NSCachedURLResponse *cachedResponse))completionHandler;//将会缓存response,根据是否NSURLRequestReloadIgnoringLocalCacheData;阻止缓存
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error;// 下载完成;首先清空数据发送下载完毕通知或者停止通知, 其次根据是否error,分别赋值不同的完成回调参数。主要看!error的情况:1.如果response从缓存里取得,那么返回nil的image;2.如果imageData存在通过分类方法sd_imageWithData生成图片2.1如果尺寸为0那么回调参数赋值错误原因2.2否则返回正确回调3.返回无数据错误完成回调
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler;// 处理相关认证

  • SDWebImageManager

此类也设计为单利,同时也提供了自定义downloader、cache初始化。其中单利默认采用SDWebImageDownloader下载图片SDImageCache存取图片。

主要类解析:
- (id <SDWebImageOperation>)downloadImageWithURL:(NSURL *)url
options:(SDWebImageOptions)options
progress:(SDWebImageDownloaderProgressBlock)progressBlock
completed:(SDWebImageCompletionWithFinishedBlock)completedBlock;

  1. 转化url参数为NSURL
    2.判断(url是否在失败池&&operations不包括包括SDWebImageRetryFailed)|| url有错,直接返回错误结束任务。
    3.将创建好的operation加入到runningOperations
  2. 用[SDImageCache queryDiskCacheForKey]查找缓存图片,回调处理
    4.1任务取消,结束
    4.2 ((!image || options & SDWebImageRefreshCached) && (![self.delegate respondsToSelector:@selector(imageManager:shouldDownloadImageForURL:)] || [self.delegate imageManager:self shouldDownloadImageForURL:url])) 处理
    4.2.1如果图片存在并且operations包括SDWebImageRefreshCached,先回调完成block返回数据;再组合operations 利用SDImageDownloader下载图片,回调处理
    4.2.1.1 operation取消,不做任何操作
    4.2.1.2 下载错误,将此URL添加到失败池,返回完成回调但会错误。
    4.2.1.3 除此只玩的相关处理:1.options & SDWebImageRetryFailed 移除失败池的当前url2.options & SDWebImageRefreshCached && image && !downloadedImage 未做操作3.(downloadedImage && (!downloadedImage.images || (options & SDWebImageTransformAnimatedImage)) && [self.delegate respondsToSelector:@selector(imageManager:transformDownloadedImage:withURL:) DownloadImage存在&&(downloadimage没有不是GIF||做过处理)
    从代理中获取处理过的图片,如果图片存在并且下载完全完成,储存图片,回调完成block。
    4.2.1.4 下载图片存在&&下载已经完成 缓存图片 回调完成block
    4.2.1.4 全部完成下载,移除执行操作池当前操作
    4.2.2 如果image存在 回调完成block,移除执行操作池当前操作
    4.2.3否则回调block,SDImageCacheTypeNone
  • SDWebImagePrefetcher

利用SDWebImageManager 进行预下载多个图片

  • UIImageView+WebCache

快速设置网络图片的UIImageView分类。

我们只需要看,他的“全能初始化方法”
- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletionBlock)completedBlock

1.取消当前所有获取image任务
2.根据是都延迟赋值placeholder赋值
3.[SDWebImageManager.sharedManager downloadImageWithURL:url options:options progress:progressBlock completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) 查询或者下载图片
4.回调中根据选项处理图片。
从底层看到这里其实这部分的代码就so easy了,就不细说了。

关于其他工具类,和分类就不一一记录了。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,425评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,058评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,186评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,848评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,249评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,554评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,830评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,536评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,239评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,505评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,004评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,346评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,999评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,060评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,821评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,574评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,480评论 2 267

推荐阅读更多精彩内容