IOSiOS那些事iOS框架

OpenSource-SDWebImage

2017-03-14  本文已影响2249人  MasonFu

SDWebImage在iOS开发中是比较常见的一个开源库,实现的网络及本地图片的异步加载及缓存功能。主流的图片格式基本都支持,甚至包括GIF和WebP。主要特性有:常用UI控件类别支持、异步加载、异步的内存与磁盘缓存周期自动化处理、后台解码、同一URL地址图片不会重复加载、无效的URL地址图片不会反复加载、非阻塞主线程、良好的性能等。关于这个开源库的源代码解析网上也有很多,这篇文章所呈现的,是基于我自己的学习和理解

类图

下图是官方文档中提供的SDWebImage库的类图,详细展现了该库的功能模块的划分和构成,可以整体上对这个库的层次结构有个了解

SDWebImageClassDiagram.png

时序图

下图是官方文档中提供的一张典型的图片加载过程,从调用category的sd_setImageWithURL接口到最后调用ImageView的setImage方法的完成图片加载的整个过程。那就以这张图为思路,逐步拆解分析和学习吧

SDWebImageSequenceDiagram.png

Category

SDWebImage主要通过category+block的方式提供接口调用,实现对原有工程代码无侵入,同时图片的加载进度及完成事件都以block方式回调,使用便捷,例如最常见的sd_setImageWithURL方法簇,在UIKit框架中的基础视图和常用控件中都有category的实现,例如UIImageView、UIView、UIButton、UIImage等。下面是几个比较常用的类别方法,最终都会调用到UIView (WebCache)sd_internalSetImageWithURL核心方法中去

    - (void)sd_setImageWithURL:(nullable NSURL *)url
      placeholderImage:(nullable UIImage *)placeholder;
      
    - (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
              progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
             completed:(nullable SDExternalCompletionBlock)completedBlock;
     
    ............

Image URL

调用方式知道了,是category,那接下来第一个问题,就是我们请求加载的图片地址,是以什么方式保存管理的呢?答案是Runtime中的objc_setAssociatedObjectobjc_getAssociatedObject方法,在运行时动态的将url值绑定到具体的对象(例如ImageView)中,以imageURLKey全局变量作为绑定值的key,代码如下:

static char imageURLKey;
...
objc_setAssociatedObject(self, &imageURLKey, url, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
...
objc_getAssociatedObject(self, &imageURLKey);

类似的动态值绑定还有TAG_ACTIVITY_INDICATOR、TAG_ACTIVITY_STYLE、TAG_ACTIVITY_SHOW,用以控制图片加载时是否使用Indicator,Indicator的显示风格等

Load Error

在SDWebImage中比较常见的一个问题就是载入失败了的图片如何处理,在SDWebImageManager核心类中,发生过载入错误的图片URL会存入failedURLs中,在每次执行下载图片请求前会检测其URL是否曾经失败过,如果失败过并且没有设置失败重试标志(SDWebImageRetryFailed),SDWebImageManager会直接调用completedBlock返回错误,不会继续后面的网络下载操作

Cache

SDWebImageManager执行正式下载之前,会先通过Cache机制查找本地是否已经存在所请求的图片(以URL String做为key来匹配),保证最优性能

缓存机制在SDWebImage中有着很好的支持,由SDImageCache类负责管理,例如缓存位置(内存、磁盘),缓存空间(路径、大小限制),缓存周期等

Download

在缓存SDImageCache查询结束后,下载操作之前,仍会有些预判断条件,例如缓存是否命中、是否config中设置了强制刷新标志(SDWebImageRefreshCached)、Delegate询问是否可以加载图片(imageManager:shouldDownloadImageForURL:)等,SDWebImageManager确定是否需要执行真正下载流程

SDWebImageDownloader类是SDWebImage中真正负责调度执行下载任务的核心类,主要包括以下几方面:

总结

下面这张图是我按照自己对SDWebImage的功能模块、调用流程、所属线程的理解,大致画的一张简略的示意图:

SDWebImage.png

Tips:block循环引用的解决办法

所谓循环引用,即在实例对象的block块中使用self.语法,导致self实例持有block,而block也持有了self实例,通常的做法是使用__weak达到block始终不持有self实例,解除了循环引用关系,从双向变成了单向,但仍然存在风险,例如block块在执行期间,self实例已经销毁

在SDWebImage的代码中,通过在block块中再强持有前面声明的weak self,达到只在block块运行期间持有可用的self实例,同时仍然维持了单向引用关系,基本降低了crash的风险,非常巧妙的用法,如下:

        ......
// 声明弱引用实例指针变量,使block块不再持有实例
__weak __typeof(self)wself = self;

id <SDWebImageOperation> operation = [SDWebImageManager.sharedManager loadImageWithURL:url 
      options:options 
      progress:progressBlock 
      completed:^(UIImage *image, NSData *data, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
      
  // 块中声明强引用实例变量,持有弱引用实例指针变量,达到只在block块内执行期间持有可用的self实例
    __strong __typeof (wself) sself = wself;
    
    [sself sd_removeActivityIndicator];
        ......
上一篇下一篇

猜你喜欢

热点阅读