首页 > 学院 > 开发设计 > 正文

SDWebImage 源码阅读(四)

2019-11-09 13:56:53
字体:
来源:转载
供稿:网友

这一篇我们来解决最后的问题,使用 SDWebImage 中下载图片的类 SDWebImageDownloader,这个类学习完以后,我们对 SDWebImage 的理解会更加深刻。

1. 下载图片

- (id <SDWebImageOperation>)downloadImageWithURL:(NSURL *)url options:(SDWebImageDownloaderOptions)options PRogress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageDownloaderCompletedBlock)completedBlock { __block SDWebImageDownloaderOperation *operation; __weak __typeof(self)wself = self; // 判断是否为第一次下载,并且存储 progressBlock 和 completedBlock,详情见 1.1 [self addProgressCallback:progressBlock completedBlock:completedBlock forURL:url createCallback:^{ // 第一次下载回调用这个block中的代码 // 图片下载的超时时间 NSTimeInterval timeoutInterval = wself.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 // 创建请求 NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url cachePolicy:(options & SDWebImageDownloaderUseNSURLCache ? NSURLRequestUseProtocolCachePolicy : NSURLRequestReloadIgnoringLocalCacheData) timeoutInterval:timeoutInterval]; // HTTPShouldHandleCookies 为 YES 表示将 cookies 一起发送至服务器 request.HTTPShouldHandleCookies = (options & SDWebImageDownloaderHandleCookies); // HTTPShouldUsePipelining 为 YES 可以极大的提高网络性能,但是 HTTPShouldUsePipelining 也有其局限性,就是服务器必须按照收到请求的顺序返回对应的数据 request.HTTPShouldUsePipelining = YES; // 设置头文件 if (wself.headersFilter) { request.allHTTPHeaderFields = wself.headersFilter(url, [wself.HTTPHeaders copy]); } else { request.allHTTPHeaderFields = wself.HTTPHeaders; } // 详情见 2. SDWebImageDownloaderOperation 核心类 operation = [[wself.operationClass alloc] initWithRequest:request insession:self.session options:options progress:^(NSInteger receivedSize, NSInteger expectedSize) { SDWebImageDownloader *sself = wself; if (!sself) return; __block NSArray *callbacksForURL; dispatch_sync(sself.barrierQueue, ^{ callbacksForURL = [sself.URLCallbacks[url] copy]; }); for (NSDictionary *callbacks in callbacksForURL) { dispatch_async(dispatch_get_main_queue(), ^{ SDWebImageDownloaderProgressBlock callback = callbacks[kProgressCallbackKey]; if (callback) callback(receivedSize, expectedSize); }); } } completed:^(UIImage *image, NSData *data, NSError *error, BOOL finished) { SDWebImageDownloader *sself = wself; if (!sself) return; __block NSArray *callbacksForURL; dispatch_barrier_sync(sself.barrierQueue, ^{ callbacksForURL = [sself.URLCallbacks[url] copy]; if (finished) { [sself.URLCallbacks removeObjectForKey:url]; } }); for (NSDictionary *callbacks in callbacksForURL) { SDWebImageDownloaderCompletedBlock callback = callbacks[kCompletedCallbackKey]; if (callback) callback(image, data, error, finished); } } cancelled:^{ SDWebImageDownloader *sself = wself; if (!sself) return; dispatch_barrier_async(sself.barrierQueue, ^{ [sself.URLCallbacks removeObjectForKey:url]; }); }]; // 是否压缩图片 operation.shouldDecompressImages = wself.shouldDecompressImages; if (wself.urlCredential) { operation.credential = wself.urlCredential; } else if (wself.username && wself.passWord) { operation.credential = [NSURLCredential credentialWithUser:wself.username password:wself.password persistence:NSURLCredentialPersistenceForSession]; } // 优先级 if (options & SDWebImageDownloaderHighPriority) { operation.queuePriority = NSOperationQueuePriorityHigh; } else if (options & SDWebImageDownloaderLowPriority) { operation.queuePriority = NSOperationQueuePriorityLow; } // 将 operation 加入队列中 [wself.downloadQueue addOperation:operation]; if (wself.executionOrder == SDWebImageDownloaderLIFOExecutionOrder) { // Emulate LIFO execution order by systematically adding new operations as last operation's dependency // 如果为栈模式,需要添加依赖关系,栈顶完成才能执行下一个 operation [wself.lastAddedOperation addDependency:operation]; wself.lastAddedOperation = operation; } }]; return operation;}

1.1. 存储 progressBlock 和 completedBlock

- (void)addProgressCallback:(SDWebImageDownloaderProgressBlock)progressBlock completedBlock:(SDWebImageDownloaderCompletedBlock)completedBlock forURL:(NSURL *)url createCallback:(SDWebImageNoParamsBlock)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; } // dispatch_barrier_sync : 等待在它前面的多线程执行完再执行这个 dispatch_barrier_sync(self.barrierQueue, ^{ // 判断是否是第一次下载这个url,如果是第一次下载,将存储该url BOOL first = NO; if (!self.URLCallbacks[url]) { self.URLCallbacks[url] = [NSMutableArray new]; first = YES; } // Handle single download of simultaneous download request for the same URL // 根据url从self.URLCallbacks字典中取出数组,将传入的block根据两个key(kProgressCallbackKey和kCompletedCallbackKey)copy到字典中 NSMutableArray *callbacksForURL = self.URLCallbacks[url]; NSMutableDictionary *callbacks = [NSMutableDictionary new]; if (progressBlock) callbacks[kProgressCallbackKey] = [progressBlock copy]; if (completedBlock) callbacks[kCompletedCallbackKey] = [completedBlock copy]; [callbacksForURL addObject:callbacks]; self.URLCallbacks[url] = callbacksForURL; if (first) { createCallback(); } });}

2. SDWebImageDownloaderOperation 核心类

在上面我们看到的下载图片方法,并没有涉及到核心代码,原来是通过以下初始化来进入 SDWebImageDownloaderOperation 核心类

- (id)initWithRequest:(NSURLRequest *)request inSession:(NSURLSession *)session options:(SDWebImageDownloaderOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageDownloaderCompletedBlock)completedBlock cancelled:(SDWebImageNoParamsBlock)cancelBlock;

我们进入上面初始化方法的实现中看到,在 init 方法中只是对传入参数的赋值和获取,基本上是初始化内容,但是下面有个重写的 start 方法

- (void)start { // 线程同步加锁,重置 @synchronized (self) { if (self.isCancelled) { self.finished = YES; [self reset]; return; }#if TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_4_0 // 获取系统的 application 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)]; // 获取这个后台线程的表示 UIBackgroundTaskIdentifier self.backgroundTaskId = [app beginBackgroundTaskWithExpirationHandler:^{ __strong __typeof (wself) sself = wself; // 后台下载时间到了,就会调用这个 block,如果任务仍在下载就进行取消,调用 endBackgroundTask 方法通知系统该 backgroundTaskId 停止,并把 backgroundTaskId 状态改为无效 if (sself) { [sself cancel]; [app endBackgroundTask:sself.backgroundTaskId]; sself.backgroundTaskId = UIBackgroundTaskInvalid; } }]; }#endif 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. */ // 为此任务创建会话 // 我们发送 nil 作为代理队列,所以创建 session 用于执行所有 delegate 调用和完成处理程序调用的串行操作队列。 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.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); } }#if TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_4_0 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}

SDWebImage 源码阅读终于完事了,最后一章有些不细心,等有时间重新整理一下


发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表