SDWebImage 学习笔记

一、 SDWebImage 简介

This library provides an async image downloader with cache support. For convenience, we added categories for UI elements like UIImageView , UIButton , MKAnnotationView .

本文基于SDWebImage的README及阅读源码后写成,主要是使用层次上的说明,包括一些可用的参数的说明

  • SDWebImage是Github上面一个流行的Objective-C 图片下载框架,具有以下特性:

    • Categories for UIImageView, UIButton, MKAnnotationView adding web image and cache management: 扩展了常用的UI图片类,使得加载图片和缓存变成一行代码

    • An asynchronous image downloader:提供了一个异步的图片下载器

    • An asynchronous memory + disk image caching with automatic cache expiration handling: 一个异步的缓存机制,该机制同时利用了内存和硬盘, 并且会自动处理过期的缓存

    • A background image decompression: 后台图片压缩
      Decompressing images that are downloaded and cached can improve performance but can consume lot of memory.
      Defaults to YES. Set this to NO if you are experiencing a crash due to excessive memory consumption.

    • A guarantee that the same URL won't be downloaded several times 保证同一URL对应的图片不会被加载多次(内部保存了一个Dictionary,已存在的不会再次下载)

    • A guarantee that bogus URLs won't be retried again and again 保证了无效的URL不会一次又一次的尝试下载

    • A guarantee that main thread will never be blocked 主线程永不阻塞

    • Performances!

    • Use GCD and ARC

  • How is SDWebImage better than NSURLRequest? (Reference: How is SDWebImage better than NSURLRequest?

    1. NSURLCache需要从硬盘中读出来放到内存(硬盘中也有,但是是decode过的数据)
    2. Decompress的过程被强制在后台进程运行,提高UI的流畅性
    3. SDWebImage让你完全不用关心复杂且易错的HTTP缓存控制,使得缓存的读取更加的快

二、 SDWebImage使用

  1. 基本使用

    #import  <SDWebImage/UIImageView+WebCache.h>
            
    [imageView sd_setImageWithURL:[NSURL URLWithString:
               @"http://www.domain.com/path/to/image.jpg" ]
                         placeholderImage:[UIImage imageNamed:
               @"placeholder.png" ]];
    
    
  2. 支持GIF格式

    导入 FLAnimatedImage 模块依赖
    使用FLAnimatedImageView代替UIImageView

  3. 图片的更新

    若图片的URL没有变化,SDWebImage不会更新图片,会使用缓存

    若需要更新图片,有两种解决方案

    • 图片更新时URL应当更新
    • 若你无法更改服务器的URL,可以使用SDWebImageRefreshCached选项,会降低performance, 但是会更新图片
  4. 部分可选参数说明(详见 SDWebImageManager.h)

    • SDWebImageRetryFailed 失败后是否重试

      • By default, when a URL fail to be downloaded, the URL is blacklisted so the library won't keep trying.
    • SDWebImageLowPriority 图片下载是否是低优先级的,若为YES,推迟图片的下载直到必须显示的时候

      • By default, image downloads are started during UI interactions, this flags disable this feature leading to delayed download on UIScrollView deceleration for instance.
    • SDWebImageCacheMemoryOnly 是否仅在内存在进行缓存

    • SDWebImageProgressiveDownload 是否需要显示下载进度 Default:NO

    • SDWebImageRefreshCached 是否允许更新缓存的图片,参见第三点的使用。默认为NO,只有无法更改服务器的URL又需要更新图片时才使用

      • Even if the image is cached, respect the HTTP response cache control, and refresh the image from remote location if needed.
      • The disk caching will be handled by NSURLCache instead of SDWebImage leading to slight performance degradation.
      • This option helps deal with images changing behind the same request URL, e.g. Facebook graph api profile pics.
      • If a cached image is refreshed, the completion block is called once with the cached image and again with the final image.
      • Use this flag only if you can't make your URLs static with embedded cache busting parameter.
    • SDWebImageContinueInBackground 是否即使在后台运行也继续下载图片

    • SDWebImageHandleCookies WebImage的下载是否应处理Cookies,默认为YES

    • SDWebImageAllowInvalidSSLCertificates 是否允许不可信的SSL连接

    • SDWebImageHighPriority 是否将新的图片放在下载队列的首部 (默认是FIFO,若为YES,则为LIFO)

    • SDWebImageDelayPlaceholder 推迟placeHolder图片的加载直到图片加载完成

    • SDWebImageTransformAnimatedImage 执行动态图的转换

    • SDWebImageAvoidAutoSetImage 是否要在图片加载后手动设置图片到UIView中,默认为NO,除非你确实需要手动将图片设置到UIView中

    • SDWebImageScaleDownLargeImages 解压图片时是否要降低图片的分辨率

三、 SDWebImage 源码

  1. SDWebImage项目构成
    • SDWebImage

      • Downloader

        • SDWebImageDownloader.h 下载器核心类,定义了核心方法和核心配置,通过单例获取实例,下载图片
          • 核心方法

            - ( nullable SDWebImageDownloadToken *)downloadImageWithURL:( nullable   NSURL *)url
                                                           options:( SDWebImageDownloaderOptions )options
                                                          progress:( nullable   SDWebImageDownloaderProgressBlock )progressBlock
                                                         completed:( nullable   SDWebImageDownloaderCompletedBlock )completedBlock;
            
          • 核心配置

      • SDWebImageDownloaderOperation

    • Cache

      • SDImageCache 缓存核心类
      • SDImageCacheConfig 缓存配置文件
    • Utils

      • SDWebImageManager 框架核心类,包含了Downloader,Prefetcher, Cache
      • SDWebImageDecoder
      • SDWebImagePrefetcher
    • Categories 辅助方法

      • WebCache Categories 包装类,使得使用UIView图片类更加容易
      • FLAnimatedImage 对GIF动图的支持
  1. 部分核心方法剖析
- (nullable SDWebImageDownloadToken *)addProgressCallback:(SDWebImageDownloaderProgressBlock)progressBlock
                                           completedBlock:(SDWebImageDownloaderCompletedBlock)completedBlock
                                                   forURL:(nullable NSURL *)url
                                           createCallback:(SDWebImageDownloaderOperation *(^)())createCallback {
    // The URL will be used as the key to the callbacks dictionary so it cannot be nil. If it is nil immediately call the completed block with no image or data.
    if (url == nil) {
        if (completedBlock != nil) {
            completedBlock(nil, nil, nil, NO);
        }
        return nil;
    }

    __block SDWebImageDownloadToken *token = nil;

    // barrierQueue的原型  @property (SDDispatchQueueSetterSementics, nonatomic, nullable) dispatch_queue_t barrierQueue;
    // This queue is used to serialize the handling of the network responses of all the download operation in a single queue
    // 不同于执行网络请求,这个操作是用于处理接收到的来自本机的网络请求的,一个个为它们生成SDWebImageDownloadToken,加入了之后它们其实是异步执行的
    dispatch_barrier_sync(self.barrierQueue, ^{
        SDWebImageDownloaderOperation *operation = self.URLOperations[url];
        //重复的URL请求不会被加入
        if (!operation) {
            //创建回调方法,其实就是产生了一个operation,加进异步请求的NSOperation的队列
            operation = createCallback();
            self.URLOperations[url] = operation;

            __weak SDWebImageDownloaderOperation *woperation = operation;
            operation.completionBlock = ^{
              SDWebImageDownloaderOperation *soperation = woperation;
              if (!soperation) return;
              if (self.URLOperations[url] == soperation) {
                  [self.URLOperations removeObjectForKey:url];
              };
            };
        }
        id downloadOperationCancelToken = [operation addHandlersForProgress:progressBlock completed:completedBlock];

        token = [SDWebImageDownloadToken new];
        token.url = url;
        token.downloadOperationCancelToken = downloadOperationCancelToken;
    });

    return token;

- (nullable SDWebImageDownloadToken *)downloadImageWithURL:(nullable NSURL *)url
                                                   options:(SDWebImageDownloaderOptions)options
                                                  progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
                                                 completed:(nullable SDWebImageDownloaderCompletedBlock)completedBlock {
    __weak SDWebImageDownloader *wself = self;
    
    //调用另一个方法,这个方法主要是写了createCallBlock(这是一个返回SDWebImageDownloaderOperation* 没有参数的方法块,用于创建一个下载的操作(请求),加入队列中异步执行)
    return [self addProgressCallback:progressBlock completedBlock:completedBlock forURL:url createCallback:^SDWebImageDownloaderOperation *{
        
        //把weakself 赋值给strongself
        __strong __typeof (wself) sself = wself;
        NSTimeInterval timeoutInterval = sself.downloadTimeout;
        if (timeoutInterval == 0.0) {
            timeoutInterval = 15.0;
        }

        // In order to prevent from potential duplicate caching (NSURLCache + SDImageCache) we disable the cache for image requests if told otherwise(原注释)
        // 只采用一个缓存机制 NSCache或SDImageCache
        NSURLRequestCachePolicy cachePolicy = NSURLRequestReloadIgnoringLocalCacheData;
        if (options & SDWebImageDownloaderUseNSURLCache) {
            if (options & SDWebImageDownloaderIgnoreCachedResponse) {
                cachePolicy = NSURLRequestReturnCacheDataDontLoad;
            } else {
                cachePolicy = NSURLRequestUseProtocolCachePolicy;
            }
        }
        
        // 创建一个Request请求
        NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url cachePolicy:cachePolicy timeoutInterval:timeoutInterval];
        
        request.HTTPShouldHandleCookies = (options & SDWebImageDownloaderHandleCookies);
        request.HTTPShouldUsePipelining = YES;
        if (sself.headersFilter) {
            request.allHTTPHeaderFields = sself.headersFilter(url, [sself.HTTPHeaders copy]);
        }
        else {
            request.allHTTPHeaderFields = sself.HTTPHeaders;
        }
        //创建一个NSOperation, 一个NSOperation负责一个图片的加载
        SDWebImageDownloaderOperation *operation = [[sself.operationClass alloc] initWithRequest:request inSession:sself.session options:options];
        operation.shouldDecompressImages = sself.shouldDecompressImages;
        
        if (sself.urlCredential) {
            operation.credential = sself.urlCredential;
        } else if (sself.username && sself.password) {
            operation.credential = [NSURLCredential credentialWithUser:sself.username password:sself.password persistence:NSURLCredentialPersistenceForSession];
        }
        
        if (options & SDWebImageDownloaderHighPriority) {
            operation.queuePriority = NSOperationQueuePriorityHigh;
        } else if (options & SDWebImageDownloaderLowPriority) {
            operation.queuePriority = NSOperationQueuePriorityLow;
        }
        
          // 把operation加入NSOperationQueue中,这个Queue是异步的
        [sself.downloadQueue addOperation:operation];
        if (sself.executionOrder == SDWebImageDownloaderLIFOExecutionOrder) {
            // Emulate LIFO execution order by systematically adding new operations as last operation's dependency
            [sself.lastAddedOperation addDependency:operation];
            sself.lastAddedOperation = operation;
        }

        return operation;
    }];
}

四、 其他

  • 单例方法均采用了 dispatch_once, 既保证了线程安全性,也保证了效率
+ (nonnull instancetype)sharedDownloader {
 
    static dispatch_once_t once;
 
    static id instance;
 
    dispatch_once(&once, ^{
 
        instance = [self new];
 
    });
 
    return instance;
 
}
  • 向系统注册通知,接收到内存不足或进入后台的通知时进行相应的处理
        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(clearMemory)
                                                     name:UIApplicationDidReceiveMemoryWarningNotification
                                                   object:nil];

        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(deleteOldFiles)
                                                     name:UIApplicationWillTerminateNotification
                                                   object:nil];

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

推荐阅读更多精彩内容