SDWebImageDownloaderOperation 是下载任务处理者

SDWebImageDownloaderOperation 继承自NSOperation ,并且实现了 <SDWebImageOperation, NSURLSessionTaskDelegate, NSURLSessionDataDelegate> 这三个协议。继承 NSOperation 的子类执行任务的代码都写在 - (void)start; 或者 - (void)main; 中,我们就从 SDWebImageDownloaderOperation 重写的 - (void)start; 方法入手。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#if TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_4_0
Class UIApplicationClass = NSClassFromString(@"UIApplication");
BOOL hasApplication = UIApplicationClass && [UIApplicationClass respondsToSelector:@selector(sharedApplication)];
if (hasApplication && [self shouldContinueWhenAppEntersBackground]) {
__weak __typeof__ (self) wself = self;
UIApplication * app = [UIApplicationClass performSelector:@selector(sharedApplication)];
self.backgroundTaskId = [app beginBackgroundTaskWithExpirationHandler:^{
__strong __typeof (wself) sself = wself;
if (sself) {
[sself cancel];
[app endBackgroundTask:sself.backgroundTaskId];
sself.backgroundTaskId = UIBackgroundTaskInvalid;
}
}];
}
#endif

这段代码的意思是,如果程序进入后台会给程序一段时间,完成未完成的任务,如果时间到了任务还是没有完成则取消这个任务。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
NSURLSession *session = self.unownedSession;
if (!self.unownedSession) {
NSURLSessionConfiguration *sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration];
sessionConfig.timeoutIntervalForRequest = 15;
/**
* Create the session for this task
* We send nil as delegate queue so that the session creates a serial operation queue for performing all delegate
* method calls and completion handler calls.
*/
self.ownedSession = [NSURLSession sessionWithConfiguration:sessionConfig
delegate:self
delegateQueue:nil];
session = self.ownedSession;
}
self.dataTask = [session dataTaskWithRequest:self.request];
self.executing = YES;
self.thread = [NSThread currentThread];

self.unownedSession 是从 SDWebImageDownloader 中传进来的,而如果没有传进来 self.unownedSession 则自己创建一个 self.ownedSession,这个 self.ownedSession 设置的代理是自己,回调的代理方法直接调用这个类里的, 而self.unownedSession 传进来的这种,代理方法就是通过上面介绍过的方式调用到这个类的。上面注释的意思是为 task 创建一个 session,delegateQueue 中传入一个 nil,这样 session 就会创建一个串行的操作队列来执行所有的代理方法和完成处理的调用。
继续看代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[self.dataTask resume];
if (self.dataTask) {
if (self.progressBlock) {
self.progressBlock(0, NSURLResponseUnknownLength);
}
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStartNotification object:self];
});
}
else {
if (self.completedBlock) {
self.completedBlock(nil, nil, [NSError errorWithDomain:NSURLErrorDomain code:0 userInfo:@{NSLocalizedDescriptionKey : @"Connection can't be initialized"}], YES);
}
}

resume 开启这个任务,调用下 self.progressBlock 传入初始的值,然后在主线程发送一个开始下载的通知,如果没有 self.dataTask 则调用 self.completedBlock 返回一个 NSError
下面我们在简单说下 SDWebImageDownloaderOperation 类中这两个 NSURLSessionTaskDelegateNSURLSessionDataDelegate 协议的代理方法。

NSURLSessionDataDelegate

1
2
3
4
5
#pragma mark NSURLSessionDataDelegate
- (void)URLSession:(NSURLSession *)session
dataTask:(NSURLSessionDataTask *)dataTask
didReceiveResponse:(NSURLResponse *)response
completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler

在这个方法里检查下 response 的状态码,不正确的话取消任务,completedBlock 回调 NSError 。正确的话,获取下载数据的总大小 expectedContentLength ,并调用 self.progressBlock 。还会创建保存数据流的 NSMutableData 对象,self.imageData = [[NSMutableData alloc] initWithCapacity:expected];

1
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data

将每次接收到的数据 data 拼接到之前创建好的 self.imageData 中去,[self.imageData appendData:data]; 。如果 option 的要求是 SDWebImageDownloaderProgressiveDownload 则在这里把已有的数据 self.imageData 转成 image ,通过 self.completedBlock 回调出去,注意 finished 参数是 NO。

1
2
3
if (self.progressBlock) {
self.progressBlock(self.imageData.length, self.expectedSize);
}

调用 self.progressBlock 将下载进度回调出去。

NSURLSessionTaskDelegate

1
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error;

请求完成的回调方法,有 error 则 self.completedBlock(nil, nil, error, YES); ,没有则将 self.imageData 转成 image 回调出去 completionBlock(image, self.imageData, nil, YES); ,当然这里面涉及很多处理的细节和其他情况的判断,就先不说了。

1
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler;

这段则是针对不同的鉴定场景返回不一样的策略。

至此,SDWebImageDownloaderOperation 中的这些代理方法就简单的介绍完了。

相关阅读:
SDWebImage 源码阅读记录(一)
SDWebImageManager 源码阅读记录(二)
SDWebImageDownloader 源码阅读记录(三)
SDWebImageDownloaderOperation 源码阅读记录(四)
SDWebImageCache 源码阅读记录(五)
SDWebImage相关类 源码阅读记录(六)