SDWebImage 源码笔记

目录

  • 简介

    • 设计目的

    • 特性

    • SDWebImage 与其他框架的对比

    • 常见问题

    • 用法

    • SDWebImage 4.0 迁移指南

  • 实现原理

    • 架构图

    • 流程图

    • 目录结构

    • 核心逻辑

  • 实现细节

    • 1. 图片下载

      • 1.1 SDWebImageDownloader

      • 1.2 SDWebImageDownloader

    • 2. 图片缓存——SDImageCache

    • 3. 图片加载管理器——SDWebImageManager

    • 4. 设置 UIImageView 的图片——UIImageView+WebCache

  • 知识点

  • 收获与疑问

  • 启发与实践

  • 延伸阅读

一、简介

1. 设计目的

SDWebImage 提供了 UIImageView、UIButton 、MKAnnotationView 的图片下载分类,只要一行代码就可以实现图片异步下载和缓存功能。这样开发者就无须花太多精力在图片下载细节上,专心处理业务逻辑。

2. 特性

  • 提供 UIImageView, UIButton, MKAnnotationView 的分类,用来显示网络图片,以及缓存管理

  • 异步下载图片

  • 异步缓存(内存+磁盘),并且自动管理缓存有效性

  • 后台图片解压缩

  • 同一个 URL 不会重复下载

  • 自动识别无效 URL,不会反复重试

  • 不阻塞主线程

  • 高性能

  • 使用 GCD 和 ARC

  • 支持多种图片格式(包括 WebP 格式)

  • 支持动图(GIF)

    • 4.0 之前的动图效果并不是太好

    • 4.0 以后基于 FLAnimatedImage加载动图

注:本文选读的代码是 3.7.3 版本的,所以动图加载还不支持 FLAnimatedImage。

3. SDWebImage 与其他框架的对比

利益相关:以下两篇文章都是 SDWebImage 的维护者所写,具有一定的主观性,仅供参考。

4. 常见问题

  • 问题 1:使用 UITableViewCell 中的 imageView 加载不同尺寸的网络图片时会出现尺寸缩放问题

解决方案:自定义 UITableViewCell,重写 -layoutSubviews 方法,调整位置尺寸;或者直接弃用 UITableViewCell 的 imageView,自己添加一个 imageView 作为子控件。

  • 问题 2:图片刷新问题:SDWebImage 在进行缓存时忽略了所有服务器返回的 caching control 设置,并且在缓存时没有做时间限制,这也就意味着图片 URL 必须是静态的了,要求服务器上一个 URL 对应的图片内容不允许更新。但是如果存储图片的服务器不由自己控制,也就是说 图片内容更新了,URL 却没有更新,这种情况怎么办?

解决方案:在调用 sd_setImageWithURL: placeholderImage: options:方法时设置 options 参数为 SDWebImageRefreshCached,这样虽然会降低性能,但是下载图片时会照顾到服务器返回的 caching control。

  • 问题 3:在加载图片时,如何添加默认的 progress indicator ?

解决方案:在调用 -sd_setImageWithURL:方法之前,先调用下面的方法:

  [imageView sd_setShowActivityIndicatorView:YES];

  [imageView sd_setIndicatorStyle:UIActivityIndicatorViewStyleGray];

5. 用法

5.1 UITableView 中使用 UIImageView+WebCache

UITabelViewCell 中的 UIImageView 控件直接调用 sd_setImageWithURL: placeholderImage:方法即可

5.2 使用回调 blocks

在 block 中得到图片下载进度和图片加载完成(下载完成或者读取缓存)的回调,如果你在图片加载完成前取消了请求操作,就不会收到成功或失败的回调

1
2
3
4
5
     [cell.imageView sd_setImageWithURL:[NSURL URLWithString:@ "http://www.domain.com/path/to/image.jpg" ]
                       placeholderImage:[UIImage imageNamed:@ "placeholder.png" ]
                              completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) {
                                     ... completion code here ...
                                  }];

5.3 SDWebImageManager 的使用

UIImageView(WebCache) 分类的核心在于 SDWebImageManager 的下载和缓存处理,SDWebImageManager将图片下载和图片缓存组合起来了。SDWebImageManager也可以单独使用。

1
2
3
4
5
6
7
8
9
10
11
     SDWebImageManager *manager = [SDWebImageManager sharedManager];
     [manager loadImageWithURL:imageURL
                       options:0
                      progress:^(NSInteger receivedSize, NSInteger expectedSize) {
                             // progression tracking code
                      }
                      completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
                         if  (image) {
                             // do something with image
                         }
                      }];

5.4 单独使用 SDWebImageDownloader 异步下载图片

我们还可以单独使用 SDWebImageDownloader 来下载图片,但是图片内容不会缓存。

1
2
3
4
5
6
7
8
9
10
11
     SDWebImageDownloader *downloader = [SDWebImageDownloader sharedDownloader];
     [downloader downloadImageWithURL:imageURL
                              options:0
                             progress:^(NSInteger receivedSize, NSInteger expectedSize) {
                                 // progression tracking code
                             }
                            completed:^(UIImage *image, NSData *data, NSError *error, BOOL finished) {
                                 if  (image && finished) {
                                     // do something with image
                                 }
                             }];

5.5 单独使用 SDImageCache 异步缓存图片

SDImageCache 支持内存缓存和异步的磁盘缓存(可选),如果你想单独使用 SDImageCache 来缓存数据的话,可以使用单例,也可以创建一个有独立命名空间的 SDImageCache 实例。

添加缓存的方法:

1
     [[SDImageCache sharedImageCache] storeImage:myImage forKey:myCacheKey];

默认情况下,图片数据会同时缓存到内存和磁盘中,如果你想只要内存缓存的话,可以使用下面的方法:

1
     [[SDImageCache sharedImageCache] storeImage:myImage forKey:myCacheKey toDisk:NO];

读取缓存时可以使用 queryDiskCacheForKey:done: 方法,图片缓存的 key 是唯一的,通常就是图片的 absolute URL。

1
2
3
4
     SDImageCache *imageCache = [[SDImageCache alloc] initWithNamespace:@ "myNamespace" ];
     [imageCache queryDiskCacheForKey:myCacheKey done:^(UIImage *image) {
         // image is not nil if image was found
     }];

5.6 自定义缓存 key

有时候,一张图片的 URL 中的一部分可能是动态变化的(比如获取权限上的限制),所以我们只需要把 URL 中不变的部分作为缓存用的 key。

1
2
3
4
     SDWebImageManager.sharedManager.cacheKeyFilter = ^(NSURL *url) {
             url = [[NSURL alloc] initWithScheme:url.scheme host:url.host path:url.path];
             return  [url absoluteString];
         };

6. SDWebImage 4.0 迁移指南

按照版本号惯例(Semantic Versioning),从版本号可以看出 SDWebImage 4.0 是一个大版本,在结构上和 API 方面都有所改动。

除了 iOS 和 tvOS 之外,SDWebImage 4.0 还支持更多的平台——watchOS 和 Max OS X。

借助 FLAnimatedImage 在动图支持上做了改进,尤其是 GIF。

二、实现原理

1. 架构图(UML 类图)

SDWebImageClassDiagram.png

2. 流程图(方法调用顺序图)

SDWebImageSequenceDiagram.png

3. 目录结构

  • Downloader

    • SDWebImageDownloader

    • SDWebImageDownloaderOperation

  • Cache

    • SDImageCache

  • Utils

    • SDWebImageManager

    • SDWebImageDecoder

    • SDWebImagePrefetcher

  • Categories

    • UIView+WebCacheOperation

    • UIImageView+WebCache

    • UIImageView+HighlightedWebCache

    • UIButton+WebCache

    • MKAnnotationView+WebCache

    • NSData+ImageContentType

    • UIImage+GIF

    • UIImage+MultiFormat

    • UIImage+WebP

  • Other

    • SDWebImageOperation(协议)

    • SDWebImageCompat(宏定义、常量、通用函数)

微信图片_20170511185123.png

4. 核心逻辑

下载 Source code(3.7.3),运行 pod install,然后打开 SDWebImage.xcworkspace,先 run 起来感受一下。

在了解细节之前我们先大概浏览一遍主流程,也就是最核心的逻辑。

我们从 MasterViewController 中的 [cell.imageView sd_setImageWithURL:url placeholderImage:placeholderImage]; 开始看起。

经过层层调用,直到 UIImageView+WebCache 中最核心的方法 sd_setImageWithURL: placeholderImage: options: progress: completed:。该方法中,主要做了以下几件事:

  • 取消当前正在进行的加载任务 operation

  • 设置 placeholder

  • 如果 URL 不为 nil,就通过 SDWebImageManager 单例开启图片加载任务 operation,SDWebImageManager 的图片加载方法中会返回一个 SDWebImageCombinedOperation 对象,这个对象包含一个 cacheOperation 和一个 cancelBlock。

SDWebImageManager 的图片加载方法 downloadImageWithURL:options:progress:completed: 中会先拿图片缓存的 key (这个 key 默认是图片 URL)去 SDImageCache 单例中读取内存缓存,如果有,就返回给 SDWebImageManager;如果内存缓存没有,就开启异步线程,拿经过 MD5 处理的 key 去读取磁盘缓存,如果找到磁盘缓存了,就同步到内存缓存中去,然后再返回给 SDWebImageManager。

如果内存缓存和磁盘缓存中都没有,SDWebImageManager 就会调用 SDWebImageDownloader 单例的 -downloadImageWithURL: options: progress: completed: 方法去下载,该会先将传入的 progressBlock 和 completedBlock 保存起来,并在第一次下载该 URL 的图片时,创建一个  NSMutableURLRequest 对象和一个 SDWebImageDownloaderOperation 对象,并将该 SDWebImageDownloaderOperation 对象添加到 SDWebImageDownloader 的downloadQueue 来启动异步下载任务。

SDWebImageDownloaderOperation 中包装了一个 NSURLConnection 的网络请求,并通过 runloop 来保持 NSURLConnection 在 start 后、收到响应前不被干掉,下载图片时,监听 NSURLConnection 回调的 -connection:didReceiveData: 方法中会负责 progress 相关的处理和回调,- connectionDidFinishLoading: 方法中会负责将 data 转为 image,以及图片解码操作,并最终回调 completedBlock。

SDWebImageDownloaderOperation 中的图片下载请求完成后,会回调给 SDWebImageDownloader,然后 SDWebImageDownloader 再回调给 SDWebImageManager,SDWebImageManager 中再将图片分别缓存到内存和磁盘上(可选),并回调给 UIImageView,UIImageView 中再回到主线程设置 image 属性。至此,图片的下载和缓存操作就圆满结束了。

当然,SDWebImage 中还有很多细节可以深挖,包括一些巧妙设计和知识点,接下来再看看SDWebImage 中的实现细节。

三、实现细节

注:为了节省篇幅,这里使用伪代码的方式来解读,具体的阅读注解见 ShannonChenCHN/SDWebImage-3.7.3。

从上面的核心逻辑分析可以看出,SDWebImage 最核心的功能也就是以下 4 件事:

  • 下载(SDWebImageDownloader)

  • 缓存(SDImageCache)

  • 将缓存和下载的功能组合起来(SDWebImageManager)

  • 封装成 UIImageView 等类的分类方法(UIImageView+WebCache 等)

1. 图片下载

1.1 SDWebImageDownloader

SDWebImageDownloader 继承于 NSObject,主要承担了异步下载图片和优化图片加载的任务。

几个问题

  • 如何实现异步下载,也就是多张图片同时下载?

  • 如何处理同一张图片(同一个 URL)多次下载的情况?

枚举定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 下载选项
typedef NS_OPTIONS(NSUInteger, SDWebImageDownloaderOptions) {
     SDWebImageDownloaderLowPriority = 1 << 0,
     SDWebImageDownloaderProgressiveDownload = 1 << 1,
     SDWebImageDownloaderUseNSURLCache = 1 << 2,
     SDWebImageDownloaderIgnoreCachedResponse = 1 << 3,
     SDWebImageDownloaderContinueInBackground = 1 << 4,
     SDWebImageDownloaderHandleCookies = 1 << 5,
     SDWebImageDownloaderAllowInvalidSSLCertificates = 1 << 6,
     SDWebImageDownloaderHighPriority = 1 << 7,
};
 
// 下载任务执行顺序
typedef NS_ENUM(NSInteger, SDWebImageDownloaderExecutionOrder) {
     SDWebImageDownloaderFIFOExecutionOrder,  // 先进先出
     SDWebImageDownloaderLIFOExecutionOrder   // 后进先出
};

.h 文件中的属性:

1
2
3
4
5
6
7
8
9
@property (assign, nonatomic) BOOL shouldDecompressImages;   // 下载完成后是否需要解压缩图片,默认为 YES
@property (assign, nonatomic) NSInteger maxConcurrentDownloads;
@property (readonly, nonatomic) NSUInteger currentDownloadCount;
@property (assign, nonatomic) NSTimeInterval downloadTimeout;
@property (assign, nonatomic) SDWebImageDownloaderExecutionOrder executionOrder;
 
@property (strong, nonatomic) NSString *username;
@property (strong, nonatomic) NSString *password;
@property (nonatomic, copy) SDWebImageDownloaderHeadersFilterBlock headersFilter;

.m 文件中的属性:

1
2
3
4
5
6
@property (strong, nonatomic) NSOperationQueue *downloadQueue;  // 图片下载任务是放在这个 NSOperationQueue 任务队列中来管理的
@property (weak, nonatomic) NSOperation *lastAddedOperation;
@property (assign, nonatomic) Class operationClass;
@property (strong, nonatomic) NSMutableDictionary *HTTPHeaders;
@property (SDDispatchQueueSetterSementics, nonatomic) dispatch_queue_t barrierQueue;
@property (strong, nonatomic) NSMutableDictionary *URLCallbacks;  // 图片下载的回调 block 都是存储在这个属性中,该属性是一个字典,key 是图片的 URL,value 是一个数组,包含每个图片的多组回调信息。用 JSON 格式表示的话,就是下面这种形式:

.h 文件中方法

1
2
3
4
5
6
7
8
9
10
11
12
13
+ (SDWebImageDownloader *)sharedDownloader;
 
- (void)setValue:(NSString *)value forHTTPHeaderField:(NSString *)field;
- (NSString *)valueForHTTPHeaderField:(NSString *)field;
 
- (void)setOperationClass:(Class)operationClass;  // 创建 operation 用的类
 
- (id)downloadImageWithURL:(NSURL *)url
                                          options:(SDWebImageDownloaderOptions)options
                                         progress:(SDWebImageDownloaderProgressBlock)progressBlock
                                        completed:(SDWebImageDownloaderCompletedBlock)completedBlock;
 
- (void)setSuspended:(BOOL)suspended;

.m 文件中的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// Lifecycle
+ (void)initialize;
+ (SDWebImageDownloader *)sharedDownloader;
- init;
- (void)dealloc;
 
// Setter and getter
- (void)setValue:(NSString *)value forHTTPHeaderField:(NSString *)field;
- (NSString *)valueForHTTPHeaderField:(NSString *)field;
- (void)setMaxConcurrentDownloads:(NSInteger)maxConcurrentDownloads;
- (NSUInteger)currentDownloadCount;
- (NSInteger)maxConcurrentDownloads;
- (void)setOperationClass:(Class)operationClass;
 
// Download
- (id)downloadImageWithURL:(NSURL *)url
                                          options:(SDWebImageDownloaderOptions)options
                                         progress:(SDWebImageDownloaderProgressBlock)progressBlock
                                        completed:(SDWebImageDownloaderCompletedBlock)completedBlock;
- (void)addProgressCallback:(SDWebImageDownloaderProgressBlock)progressBlock
           andCompletedBlock:(SDWebImageDownloaderCompletedBlock)completedBlock
                      forURL:(NSURL *)url
              createCallback:(SDWebImageNoParamsBlock)createCallback;
 
// Download queue            
- (void)setSuspended:(BOOL)suspended;

具体实现:

先看看 +initialize 方法,这个方法中主要是通过注册通知 让SDNetworkActivityIndicator 监听下载事件,来显示和隐藏状态栏上的 network activity indicator。为了让 SDNetworkActivityIndicator 文件可以不用导入项目中来(如果不要的话),这里使用了 runtime 的方式来实现动态创建类以及调用方法。

1
2
3
4
5
6
7
8
+ (void)initialize {
     if  (NSClassFromString(@ "SDNetworkActivityIndicator" )) {
         id activityIndicator = [NSClassFromString(@ "SDNetworkActivityIndicator" ) performSelector:NSSelectorFromString(@ "sharedActivityIndicator" )];
 
         # 先移除通知观察者 SDNetworkActivityIndicator
         # 再添加通知观察者 SDNetworkActivityIndicator
     }
}

+sharedDownloader 方法中调用了 -init 方法来创建一个单例,-init方法中做了一些初始化设置和默认值设置,包括设置最大并发数(6)、下载超时时长(15s)等。

1
2
3
4
5
6
7
8
- (id)init {
     #设置下载 operation 的默认执行顺序(先进先出还是先进后出)
     #初始化 _downloadQueue(下载队列),_URLCallbacks(下载回调 block 的容器),_barrierQueue(GCD 队列)
     #设置 _downloadQueue 的队列最大并发数默认值为 6
     #设置 _HTTPHeaders 默认值 
     #设置默认下载超时时长 15s 
     ...
}

除了以上两个方法之外,这个类中最核心的方法就是 - downloadImageWithURL: options: progress: completed: 方法,这个方法中首先通过调用 -addProgressCallback: andCompletedBlock: forURL: createCallback: 方法来保存每个 url 对应的回调 block,-addProgressCallback: ... 方法先进行错误检查,判断 URL 是否为空,然后再将 URL 对应的 progressBlock 和 completedBlock 保存到 URLCallbacks 属性中去。

URLCallbacks 属性是一个 NSMutableDictionary 对象,key 是图片的 URL,value 是一个数组,包含每个图片的多组回调信息。用 JSON 格式表示的话,就是下面这种形式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{
     "callbacksForUrl1" : [
         {
             "kProgressCallbackKey" "progressCallback1_1" ,
             "kCompletedCallbackKey" "completedCallback1_1"
         },
         {
             "kProgressCallbackKey" "progressCallback1_2" ,
             "kCompletedCallbackKey" "completedCallback1_2"
         }
     ],
     "callbacksForUrl2" : [
         {
             "kProgressCallbackKey" "progressCallback2_1" ,
             "kCompletedCallbackKey" "completedCallback2_1"
         },
         {
             "kProgressCallbackKey" "progressCallback2_2" ,
             "kCompletedCallbackKey" "completedCallback2_2"
         }
     ]
}

这里有个细节需要注意,因为可能同时下载多张图片,所以就可能出现多个线程同时访问 URLCallbacks 属性的情况。为了保证线程安全,所以这里使用了 dispatch_barrier_sync 来分步执行添加到 barrierQueue 中的任务,这样就能保证同一时间只有一个线程能对 URLCallbacks 进行操作。

1
2
3
4
5
6
7
8
9
10
11
12
- (void)addProgressCallback:(SDWebImageDownloaderProgressBlock)progressBlock andCompletedBlock:(SDWebImageDownloaderCompletedBlock)completedBlock forURL:(NSURL *)url createCallback:(SDWebImageNoParamsBlock)createCallback {
     #1. 判断 url 是否为 nil,如果为 nil 则直接回调 completedBlock,返回失败的结果,然后 return,因为 url 会作为存储 callbacks 的 key
 
     #2. 处理同一个 URL 的多次下载请求(MARK: 使用 dispatch_barrier_sync 函数来保证同一时间只有一个线程能对 URLCallbacks 进行操作):
       ## 从属性 URLCallbacks(一个字典) 中取出对应 url 的 callBacksForURL(这是一个数组,因为可能一个 url 不止在一个地方下载)
       ## 如果没有取到,也就意味着这个 url 是第一次下载,那就初始化一个 callBacksForURL 放到属性 URLCallbacks 中
       ## 往数组 callBacksForURL 中添加 包装有 callbacks(progressBlock 和 completedBlock)的字典
       ## 更新 URLCallbacks 存储的对应 url 的 callBacksForURL
 
     #3. 如果这个 url 是第一次请求下载,就回调 createCallback
 
}

如果这个 URL 是第一次被下载,就要回调 createCallback,createCallback 主要做的就是创建并开启下载任务,下面是 createCallback 的具体实现逻辑:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
- (id)downloadImageWithURL:(NSURL *)url options:(SDWebImageDownloaderOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageDownloaderCompletedBlock)completedBlock {
      #1. 调用 - [SDWebImageDownloader addProgressCallback: andCompletedBlock: forURL: createCallback: ] 方法,直接把入参 url、progressBlock 和 completedBlock 传进该方法,并在第一次下载该 URL 时回调 createCallback
 
         ## createCallback 的回调处理:{
           1.1 创建下载 request ,设置 request 的 cachePolicy、HTTPShouldHandleCookies、HTTPShouldUsePipelining,以及 allHTTPHeaderFields(这个属性交由外面处理,设计的比较巧妙)
 
           1.2 创建 SDWebImageDownloaderOperation(继承自 NSOperation)
 
               ### 1.2.1 SDWebImageDownloaderOperation 的 progressBlock 回调处理 {
                         (这个 block 有两个回调参数:接收到的数据大小和预计数据大小)
                          这里用了 weak-strong dance
                          首先使用 strongSelf 强引用 weakSelf,目的是为了保住 self 不被释放  
                          然后检查 self 是否已经被释放(这里为什么先“保活”后“判空”呢?因为如果先判空的话,有可能判空后 self 就被释放了)
                          取出 url 对应的回调 block 数组(这里取的时候有些讲究,考虑了多线程问题,而且取的是 copy 的内容)
                          遍历数组,从每个元素(字典)中取出 progressBlock 进行回调       
                      }
               ### 1.2.2 SDWebImageDownloaderOperation 的 completedBlock 回调处理 {
                          (这个 block 有四个回调参数:图片 UIImage,图片数据 NSData,错误 NSError,是否结束 isFinished)
                          同样,这里也用了 weak-strong dance
                          接着,取出 url 对应的回调 block 数组
                          如果结束了(isFinished),就移除 url 对应的回调 block 数组(移除的时候也要考虑多线程问题)
                          遍历数组,从每个元素(字典)中取出 completedBlock 进行回调 
               }
               ### SDWebImageDownloaderOperation 的 cancelBlock 回调处理 {
                          同样,这里也用了 weak-strong dance
                          然后移除 url 对应的所有回调 block
               }
           1.3 设置下载完成后是否需要解压缩
           1.4 如果设置了 username 和 password,就给 operation 的下载请求设置一个 NSURLCredential  
           1.5 设置 operation 的队列优先级
           1.6 将 operation 加入到队列 downloadQueue 中,队列(NSOperationQueue)会自动管理 operation 的执行
           1.7 如果 operation 执行顺序是先进后出,就设置 operation 依赖关系(先加入的依赖于后加入的),并记录最后一个 operation(lastAddedOperation)
         }
 
      #2. 返回 createCallback 中创建的 operation(SDWebImageDownloaderOperation)
}

createCallback 方法中调用了 - [SDWebImageDownloaderOperation initWithRequest: options: progress:] 方法来创建下载任务 SDWebImageDownloaderOperation。那么,这个 SDWebImageDownloaderOperation 类究竟是干什么的呢?下一节再看。

知识点:

1.SDWebImageDownloaderOptions 枚举使用了位运算

应用:通过“与”运算符,可以判断是否设置了某个枚举选项,因为每个枚举选择项中只有一位是1,其余位都是 0,所以只有参与运算的另一个二进制值在同样的位置上也为 1,与 运算的结果才不会为 0.

1
2
3
   0101 (相当于 SDWebImageDownloaderLowPriority | SDWebImageDownloaderUseNSURLCache)
& 0100 (= 1 << 2,也就是 SDWebImageDownloaderUseNSURLCache)
= 0100 (> 0,也就意味着 option 参数中设置了 SDWebImageDownloaderUseNSURLCache)

2.dispatch_barrier_sync 函数的使用

3.weak-strong dance

4.HTTP header 的理解

5.NSOperationQueue 的使用

6.NSURLRequest 的 cachePolicy、HTTPShouldHandleCookies、HTTPShouldUsePipelining

7.NSURLCredential

8.createCallback 里面为什么要用 wself?

1
NSTimeInterval timeoutInterval = wself.downloadTimeout;

1.2 SDWebImageDownloaderOperation

每张图片的下载都会发出一个异步的 HTTP 请求,这个请求就是由 SDWebImageDownloaderOperation 管理的。

SDWebImageDownloaderOperation 继承 NSOperation,遵守 SDWebImageOperation、NSURLConnectionDataDelegate 协议。

SDWebImageOperation 协议只定义了一个方法 -cancel,用来取消 operation。

几个问题

  • 如何实现下载的网络请求?

  • 如何管理整个图片下载的过程?

  • 图片下载完成后需要做哪些处理?

.h 文件中的属性:

1
2
3
4
5
6
7
8
9
@property (strong, nonatomic, readonly) NSURLRequest *request;  // 用来给 operation 中的 connection 使用的请求
@property (assign, nonatomic) BOOL shouldDecompressImages;  // 下载完成后是否需要解压缩
@property (nonatomic, assign) BOOL shouldUseCredentialStorage; 
@property (nonatomic, strong) NSURLCredential *credential;
@property (assign, nonatomic, readonly) SDWebImageDownloaderOptions options;
@property (assign, nonatomic) NSInteger expectedSize;
@property (strong, nonatomic) NSURLResponse *response;
 
其他继承自 NSOperation 的属性(略)

.m 文件中的属性:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@property (copy, nonatomic) SDWebImageDownloaderProgressBlock progressBlock;    
@property (copy, nonatomic) SDWebImageDownloaderCompletedBlock completedBlock;
@property (copy, nonatomic) SDWebImageNoParamsBlock cancelBlock;
 
@property (assign, nonatomic, getter = isExecuting) BOOL executing;  // 覆盖了 NSOperation 的 executing
@property (assign, nonatomic, getter = isFinished) BOOL finished;   // 覆盖了 NSOperation 的 finished
@property (assign, nonatomic) NSInteger expectedSize;
@property (strong, nonatomic) NSMutableData *imageData;
@property (strong, nonatomic) NSURLConnection *connection;
@property (strong, atomic) NSThread *thread;
 
@property (assign, nonatomic) UIBackgroundTaskIdentifier backgroundTaskId;  // Xcode 的 BaseSDK 设置为 iOS 4.0 时以上使用
 
// 成员变量
size_t width, height;                   // 图片宽高
UIImageOrientation orientation;       // 图片方向
BOOL responseFromCached;

.h 文件中的方法:

1
2
3
4
5
6
7
- (id)initWithRequest:(NSURLRequest *)request
               options:(SDWebImageDownloaderOptions)options
              progress:(SDWebImageDownloaderProgressBlock)progressBlock
             completed:(SDWebImageDownloaderCompletedBlock)completedBlock
             cancelled:(SDWebImageNoParamsBlock)cancelBlock;    
 
其他继承自 NSOperation 的方法(略)

.m 文件中的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// 覆盖了父类的属性,需要重新实现属性合成方法
@synthesize executing = _executing;
@synthesize finished = _finished;
 
// Initialization
- (id)initWithRequest:(NSURLRequest *)request
               options:(SDWebImageDownloaderOptions)options
              progress:(SDWebImageDownloaderProgressBlock)progressBlock
             completed:(SDWebImageDownloaderCompletedBlock)completedBlock
             cancelled:(SDWebImageNoParamsBlock)cancelBlock;
// Operation
- (void)start;
- (void)cancel;
- (void)cancelInternalAndStop;
- (void)cancelInternal;
- (void)done;
- (void)reset;
 
// Setter and getter
- (void)setFinished:(BOOL)finished; 
cancelled:(SDWebImageNoParamsBlock)cancelBlock;
// Operation
- (void)start;
- (void)cancel;
- (void)cancelInternalAndStop;
- (void)cancelInternal;
- (void)done;
- (void)reset;
 
// Setter and getter
- (void)setFinished:(BOOL)finished; 
- (void)setExecuting:(BOOL)executing; 
- (BOOL)isConcurrent; 
 
// NSURLConnectionDataDelegate 方法
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response;  //  下载过程中的 response 回调
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data;  // NSURLConnectionDataDelegate 方法
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response;  //  下载过程中的 response 回调
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data;  // 下载过程中 data 回调
- (void)connectionDidFinishLoading:(NSURLConnection *)aConnection;  // 下载完成时回调
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error;  // 下载失败时回调
- (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse;  // 在 connection 存储 cached response 到缓存中之前调用
- (BOOL)connectionShouldUseCredentialStorage:(NSURLConnection __unused *)connection;  //  URL loader 是否应该使用 credential storage
- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge;  // connection 发送身份认证的请求之前被调用
 
// Helper
//  下载过程中的 response 回调
相关文章
相关标签/搜索