SDWebImage主要類的功能如下:
- SDWebImageManager:負(fù)責(zé)查緩存逗抑,下載圖片
- SDImageCache:查緩存弟胀,分為內(nèi)存緩存和磁盤緩存兩步
- SDWebImageDownloader:圖片下載组哩,保存回調(diào)block和構(gòu)造下載operation并加入到queue
- SDWebImageDownloaderOperation:實(shí)現(xiàn)圖片下載及處理回調(diào)
SDWebImageDownloader的實(shí)現(xiàn)順序是
- 保存回調(diào)block
- 構(gòu)建下載operation
- 加入到queue后執(zhí)行
- 開始下載
- 回調(diào)處理
這里主要通過SDWebImageDownloaderOperation分析下載和回調(diào)部分业筏,參考代碼是SDWebImage的最新版3.8.1
初始化
SDWebImageDownloader會(huì)創(chuàng)建SDWebImageDownloaderOperation,在com.hackemist.SDWebImageDownloaderBarrierQueue線程中趁啸。
- (id)initWithRequest:(NSURLRequest *)request
inSession:(NSURLSession *)session
options:(SDWebImageDownloaderOptions)options
progress:(SDWebImageDownloaderProgressBlock)progressBlock
completed:(SDWebImageDownloaderCompletedBlock)completedBlock
cancelled:(SDWebImageNoParamsBlock)cancelBlock {
if ((self = [super init])) {
//已設(shè)置:URL舶斧,緩存策略欣鳖,超時(shí)時(shí)間,不使用默認(rèn)cookie茴厉,管線化泽台,HeaderFields
_request = request;
_shouldDecompressImages = YES;
//選項(xiàng):優(yōu)先級(jí),緩存,后臺(tái)任務(wù)執(zhí)行,cookie處理以及證書認(rèn)證幾個(gè)方面,在創(chuàng)建下載操作的時(shí)候可以使用組合的選項(xiàng)來完成一些特殊的需求
_options = options;
//拷貝block 下載中,完成矾缓,取消
_progressBlock = [progressBlock copy];
_completedBlock = [completedBlock copy];
_cancelBlock = [cancelBlock copy];
_executing = NO;
_finished = NO;
_expectedSize = 0;
//NSURLSession 7.0推出替代NSURLConnection
_unownedSession = session;
responseFromCached = YES; // Initially wrong until `- URLSession:dataTask:willCacheResponse:completionHandler: is called or not called
}
return self;
}
NSURLConnection被替換成了NSURLSession怀酷、NSURLSessionConfiguration以及NSURLSessionTask的3個(gè)子類:NSURLSessionDataTask,NSURLSessionUploadTask嗜闻,NSURLSessionDownloadTask蜕依。
NSURLSession也是一組相互依賴的類,包括了NSURLRequest與NSURLCache琉雳。
一般一個(gè)請(qǐng)求創(chuàng)建過程為
- 配置會(huì)話屬性NSURLSessionConfiguration样眠,可以配置緩存,協(xié)議咐吼,cookie,以及證書策略(credential policy)
- 由一個(gè)NSURLSessionConfiguration對(duì)象來初始化一個(gè)NSURLSession對(duì)象
- 用NSURLSession對(duì)象創(chuàng)建NSURLSessionTask
簡單說就是屬性通過configuration配置商佑,然后初始化session锯茄,最后用session創(chuàng)建task來執(zhí)行。
- configuration可以跨程序共享這些信息給每個(gè)session
- session創(chuàng)建時(shí)可以指定委托回調(diào)對(duì)象
- urlrequest在task創(chuàng)建時(shí)傳入茶没,調(diào)用resume方法開始執(zhí)行請(qǐng)求操作
NSURLsessionTask 是一個(gè)抽象類肌幽,其下有 3 個(gè)實(shí)體子類可以直接使用:NSURLSessionDataTask、NSURLSessionUploadTask抓半、NSURLSessionDownloadTask喂急。這 3 個(gè)子類封裝了現(xiàn)代程序三個(gè)最基本的網(wǎng)絡(luò)任務(wù):獲取數(shù)據(jù),比如 JSON 或者 XML笛求,上傳文件和下載文件廊移。
<img src="http://img.objccn.io/issue-5/NSURLSession.png" width = "400" />
start
在SDWebImageDownloader內(nèi),operation創(chuàng)建成功后被加入到downloadQueue中探入,輪到該operation執(zhí)行時(shí)就會(huì)調(diào)用它的start方法
這里獲取數(shù)據(jù)使用的是NSURLSessionDataTask
- (void)start {
//保證只有當(dāng)前線程執(zhí)行該方法
@synchronized (self) {
//如果已經(jīng)取消狡孔,清空數(shù)據(jù)
if (self.isCancelled) {
self.finished = YES;
[self reset];
return;
}
//進(jìn)入后臺(tái)時(shí)借用系統(tǒng)時(shí)間
#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
//在SDWebImageDownloader的init方法中初始化session,配置了config和委托對(duì)象
NSURLSession *session = self.unownedSession;
if (!self.unownedSession) {
//創(chuàng)建sessionConfig蜂嗽,配置會(huì)話屬性
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.
*/
//初始化session苗膝,配置config和委托對(duì)象,同SDWebImageDownloader生成的session類似
self.ownedSession = [NSURLSession sessionWithConfiguration:sessionConfig
delegate:self
delegateQueue:nil];
session = self.ownedSession;
}
//用session創(chuàng)建task植旧,并傳入requst
self.dataTask = [session dataTaskWithRequest:self.request];
self.executing = YES;
self.thread = [NSThread currentThread];
}
//開始執(zhí)行task
[self.dataTask resume];
if (self.dataTask) {
//task創(chuàng)建成功辱揭,進(jìn)度回調(diào)
if (self.progressBlock) {
self.progressBlock(0, NSURLResponseUnknownLength);
}
dispatch_async(dispatch_get_main_queue(), ^{
//通知開始下載离唐,外部UI響應(yīng)startActivity方法
[[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStartNotification object:self];
});
}
else {
//task創(chuàng)建失敗,完成回調(diào)
if (self.completedBlock) {
self.completedBlock(nil, nil, [NSError errorWithDomain:NSURLErrorDomain code:0 userInfo:@{NSLocalizedDescriptionKey : @"Connection can't be initialized"}], YES);
}
}
#if TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_4_0
//停止在后臺(tái)的執(zhí)行
Class UIApplicationClass = NSClassFromString(@"UIApplication");
if(!UIApplicationClass || ![UIApplicationClass respondsToSelector:@selector(sharedApplication)]) {
return;
}
if (self.backgroundTaskId != UIBackgroundTaskInvalid) {
UIApplication * app = [UIApplication performSelector:@selector(sharedApplication)];
[app endBackgroundTask:self.backgroundTaskId];
self.backgroundTaskId = UIBackgroundTaskInvalid;
}
#endif
}
委托方法
由于創(chuàng)建SDWebImageDownloaderOperation時(shí)傳入了session问窃,session指定了他的持有者SDWebImageDownloader為delegate
- (SDWebImageDownloaderOperation *)operationWithTask:(NSURLSessionTask *)task {
//通過委托方法傳入的task亥鬓,用taskId找到operation
SDWebImageDownloaderOperation *returnOperation = nil;
for (SDWebImageDownloaderOperation *operation in self.downloadQueue.operations) {
if (operation.dataTask.taskIdentifier == task.taskIdentifier) {
returnOperation = operation;
break;
}
}
//返回對(duì)應(yīng)下載的operation
return returnOperation;
}
但downloader實(shí)現(xiàn)的NSURLSessionDataDelegate和NSURLSessionTaskDelegate協(xié)議,都會(huì)轉(zhuǎn)發(fā)給operation來執(zhí)行泡躯。
- NSURLSessionDelegate(繼承NSObjectProtocol):處理session級(jí)別的任務(wù)贮竟,比如授權(quán)、后臺(tái)session完成
- NSURLSessionTaskDelegate(繼承NSURLSessionDelegate:處理task級(jí)別的任務(wù)
- NSURLSessionDataDelegate(繼承NSURLSessionTaskDelegate:處理data和upload task的專屬事件
- NSURLSessionDownloadDelegate(繼承NSURLSessionTaskDelegate:處理download task的專屬事件较剃,比如下載完成咕别、百分比。
//接受到服務(wù)響應(yīng)時(shí)調(diào)用的方法
- (void)URLSession:(NSURLSession *)session
dataTask:(NSURLSessionDataTask *)dataTask
didReceiveResponse:(NSURLResponse *)response
completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler {
//設(shè)置文件預(yù)期大小或取消
}
//接收到服務(wù)器返回的數(shù)據(jù)時(shí)調(diào)用的方法
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data {
//拼接數(shù)據(jù)
}
//基于請(qǐng)求緩存數(shù)據(jù)時(shí)調(diào)用
- (void)URLSession:(NSURLSession *)session
dataTask:(NSURLSessionDataTask *)dataTask
willCacheResponse:(NSCachedURLResponse *)proposedResponse
completionHandler:(void (^)(NSCachedURLResponse *cachedResponse))completionHandler {
//緩存
}
在iOS7中写穴,delegate引入了一種全新的處理方式惰拱。像這個(gè)緩存的回調(diào)方法,傳入了block啊送,可以讓應(yīng)用在中間進(jìn)行一些操作之后來決定如何或是否繼續(xù)執(zhí)行后面的操作block偿短。
//請(qǐng)求完成時(shí)調(diào)用的方法(成功或失敗)
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
//成功block回調(diào)
}
//接收服務(wù)端挑戰(zhàn)
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler {
//完成認(rèn)證
}
cancel
取消operation分兩種情況
- (void)cancel {
@synchronized (self) {
//請(qǐng)求完成時(shí)馋没,會(huì)把thread置nil
if (self.thread) {
//請(qǐng)求已經(jīng)完成
[self performSelector:@selector(cancelInternalAndStop) onThread:self.thread withObject:nil waitUntilDone:NO];
}
else {
//請(qǐng)求未完成昔逗,執(zhí)行cancelBlock,cancel掉task篷朵,和一些數(shù)據(jù)清理工作
[self cancelInternal];
}
}
}
以上就是SDWebImageDownloaderOperation主要代碼的分析勾怒,開始和取消operation。主要是對(duì)session的一個(gè)運(yùn)用声旺,以及delegate的處理笔链。
3.8.1是6月7號(hào)剛剛發(fā)布的版本,分析完這部分對(duì)圖片的下載過程有個(gè)基本的了解腮猖,以后寫代碼多多借鑒鉴扫。