SDWebImage源码解读
SDWebImage是一个优秀的开源的第三方库,它具有以下功能:
提供UIImageView的一个分类,以支持网络图片的加载与缓存管理
- 一个异步的图片加载器
- 一个异步的内存+磁盘图片缓存
- 支持GIF图片
- 支持WebP图片
- 后台图片解压缩处理
- 确保同一个URL的图片不被下载多次
- 确保虚假的URL不会被反复加载
- 确保下载及缓存时,主线程不被阻塞
SDWebImage的核心类就是三个:
-
SDImageCache
它主要负责图片的缓存 -
SDWebImageDownloader
它主要负责图片的下载 -
SDWebImageManager
它主要负责图片的下载操作的管理。而且我们经常用到的诸如UIImageView+WebCache等控件的分类都是基于SDWebImageManager对象的。该对象将一个下载器和一个图片缓存绑定在一起,并对外提供两个只读属性来获取它们
下面我们就以我们平常的使用为入口,来对SDWebImage的源码进行分析。
一般我们使用SDWebImage用得最多的就是这个函数
- (void)sd_setImageWithURL:(nullable NSURL *)url placeholderImage:(nullable UIImage *)placeholder options:(SDWebImageOptions)options
options参数是个NS_OPTIONS类型,其中有12个参数,你可以根据你的项目需求进行选择,具体每个参数的作用我就不一一解释了,因为作者注释已经说得很清楚。
下面我们就从这个函数入口开始一步一步分析。
查看源码可以发现,最终函数掉用了
这个函数
- (void)sd_internalSetImageWithURL:(nullable NSURL *)url
placeholderImage:(nullable UIImage *)placeholder
options:(SDWebImageOptions)options
operationKey:(nullable NSString *)operationKey
setImageBlock:(nullable SDSetImageBlock)setImageBlock
progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
completed:(nullable SDExternalCompletionBlock)completedBlock {
NSString *validOperationKey = operationKey ?: NSStringFromClass([self class]);
//如果改队列已经存在,则取消下载
[self sd_cancelImageLoadOperationWithKey:validOperationKey];
objc_setAssociatedObject(self, &imageURLKey, url, OBJC_ASSOCIATION_RETAIN_NONATOMIC); //保存url
//如果不需要延迟加载默认图片,则先显示默认图片
if (!(options & SDWebImageDelayPlaceholder)) {
dispatch_main_async_safe(^{
[self sd_setImage:placeholder imageData:nil basedOnClassOrViaCustomSetImageBlock:setImageBlock];
});
}
if (url) {
// check if activityView is enabled or not
if ([self sd_showActivityIndicatorView]) {
[self sd_addActivityIndicator];
}
//1-------开始下载
__weak __typeof(self)wself = self;
id <SDWebImageOperation> operation = [SDWebImageManager.sharedManager loadImageWithURL:url options:options progress:progressBlock completed:^(UIImage *image, NSData *data, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
//下载完成的回调
__strong __typeof (wself) sself = wself;
[sself sd_removeActivityIndicator];
//如果视图已经被销毁了,则直接return
if (!sself) {
return;
}
//2--------这就是一个宏,切换到主线程
dispatch_main_async_safe(^{
if (!sself) {
return;
}
//如果image下载成功且设置了不自动给image添加上去,就直接把数据传给completeBlock处理
if (image && (options & SDWebImageAvoidAutoSetImage) && completedBlock) {
completedBlock(image, error, cacheType, url);
return;
} else if (image) {
[sself sd_setImage:image imageData:data basedOnClassOrViaCustomSetImageBlock:setImageBlock];
[sself sd_setNeedsLayout];
} else {
//没下载成功,如果设置了延迟加载默认图片,则设置默认图片
if ((options & SDWebImageDelayPlaceholder)) {
[sself sd_setImage:placeholder imageData:nil basedOnClassOrViaCustomSetImageBlock:setImageBlock];
[sself sd_setNeedsLayout];
}
}
if (completedBlock && finished) {
completedBlock(image, error, cacheType, url);
}
});
}];
//3-------将operation保存起来,并且如果存在的话会取消
[self sd_setImageLoadOperation:operation forKey:validOperationKey];
} else {
dispatch_main_async_safe(^{
[self sd_removeActivityIndicator];
if (completedBlock) {
NSError *error = [NSError errorWithDomain:SDWebImageErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey : @"Trying to load a nil url"}];
completedBlock(nil, error, SDImageCacheTypeNone, url);
}
});
}
}
一步一步看下来还是很简单的,继续往下看。
SDWebImageManager
下面再看看标记1的代码,这是SD中最重要的一个类SDWebImageManager,它负责管理下载和缓存
//标记1---------------
- (id <SDWebImageOperation>)loadImageWithURL:(nullable NSURL *)url
options:(SDWebImageOptions)options
progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
completed:(nullable SDInternalCompletionBlock)completedBlock {
// Invoking this method without a completedBlock is pointless
NSAssert(completedBlock != nil, @"If you mean to prefetch the image, use -[SDWebImagePrefetcher prefetchURLs] instead");
if ([url isKindOfClass:NSString.class]) {
url = [NSURL URLWithString:(NSString *)url];
if (![url isKindOfClass:NSURL.class]) {
url = nil;
}
//前面主要是对URL进行一个转换,防止了边界情况
//加上block以在后面修改
__block SDWebImageCombinedOperation *operation = [SDWebImageCombinedOperation new];
//加上weak防止引用循环
__weak SDWebImageCombinedOperation *weakOperation = operation;
BOOL isFailedUrl = NO;
if (url) {
//因为有可能同时有多个线程操作,所以需要线程安全访问
@synchronized (self.failedURLs) {
//有个数组保存了所有请求失败过的URL,所以判断一下是否是请求失败过的url
isFailedUrl = [self.failedURLs containsObject:url];
}
}
//如果url不正确或者是已经访问失败过的url且没有设置了重新请求失败的url
//直接返回
if (url.absoluteString.length == 0 || (!(options & SDWebImageRetryFailed) && isFailedUrl)) {
[self callCompletionBlockForOperation:operation completion:completedBlock error:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorFileDoesNotExist userInfo:nil] url:url];
return operation;
}
@synchronized (self.runningOperations) {
[self.runningOperations addObject:operation];
}
NSString *key = [self cacheKeyForURL:url];//根据url设置key
//a------根据缓存策略进行查找
operation.cacheOperation = [self.imageCache queryCacheOperationForKey:key done:^(UIImage *cachedImage, NSData *cachedData, SDImageCacheType cacheType) {
if (operation.isCancelled) {
[self safelyRemoveOperationFromRunning:operation];
return;
}
//如果没有找到缓存的image或者找到了image但是设置了需要更新缓存。 且delegate没有实现回调方法。或者delegate实现了回调方法但是返回yes
//也就是说默认情况下没有找到缓存图片都是需要下载的
if ((!cachedImage || options & SDWebImageRefreshCached) && (![self.delegate respondsToSelector:@selector(imageManager:shouldDownloadImageForURL:)] || [self.delegate imageManager:self shouldDownloadImageForURL:url])) {
if (cachedImage && options & SDWebImageRefreshCached) {
// 如果缓存图片找到且需要更新缓存则先把缓存的数据传过去
[self callCompletionBlockForOperation:weakOperation completion:completedBlock image:cachedImage data:cachedData error:nil cacheType:cacheType finished:YES url:url];
}
// 根据用户的的设置设置下载的一些参数配置
SDWebImageDownloaderOptions downloaderOptions = 0;
if (options & SDWebImageLowPriority) downloaderOptions |= SDWebImageDownloaderLowPriority;
if (options & SDWebImageProgressiveDownload) downloaderOptions |= SDWebImageDownloaderProgressiveDownload;
if (options & SDWebImageRefreshCached) downloaderOptions |= SDWebImageDownloaderUseNSURLCache;
if (options & SDWebImageContinueInBackground) downloaderOptions |= SDWebImageDownloaderContinueInBackground;
if (options & SDWebImageHandleCookies) downloaderOptions |= SDWebImageDownloaderHandleCookies;
if (options & SDWebImageAllowInvalidSSLCertificates) downloaderOptions |= SDWebImageDownloaderAllowInvalidSSLCertificates;
if (options & SDWebImageHighPriority) downloaderOptions |= SDWebImageDownloaderHighPriority;
if (options & SDWebImageScaleDownLargeImages) downloaderOptions |= SDWebImageDownloaderScaleDownLargeImages;
if (cachedImage && options & SDWebImageRefreshCached) {
// force progressive off if image already cached but forced refreshing
downloaderOptions &= ~SDWebImageDownloaderProgressiveDownload;
// ignore image read from NSURLCache if image if cached but force refreshing
downloaderOptions |= SDWebImageDownloaderIgnoreCachedResponse;
}
//b---------通过SDWebImageDownloader类开始下载,
SDWebImageDownloadToken *subOperationToken = [self.imageDownloader downloadImageWithURL:url options:downloaderOptions progress:progressBlock completed:^(UIImage *downloadedImage, NSData *downloadedData, NSError *error, BOOL finished) {
//下载完成的回调
__strong __typeof(weakOperation) strongOperation = weakOperation; //防止operation提前被释放,需要强引用一下,但是这不会造成引用循环,改引用会自动释放
if (!strongOperation || strongOperation.isCancelled) {
//如果已经取消则什么都不做
} else if (error) {
[self callCompletionBlockForOperation:strongOperation completion:completedBlock error:error url:url];
if ( error.code != NSURLErrorNotConnectedToInternet
&& error.code != NSURLErrorCancelled
&& error.code != NSURLErrorTimedOut
&& error.code != NSURLErrorInternationalRoamingOff
&& error.code != NSURLErrorDataNotAllowed
&& error.code != NSURLErrorCannotFindHost
&& error.code != NSURLErrorCannotConnectToHost) {
@synchronized (self.failedURLs) {
//如果不是因为网络等一些原因引起的错误,就把url加入failedURLS中
[self.failedURLs addObject:url];
}
}
}
else {
//正常下载的情况
if ((options & SDWebImageRetryFailed)) {
//如果是重新下载的之前一个标示了错误的URL,就把它移除
@synchronized (self.failedURLs) {
[self.failedURLs removeObject:url];
}
}
BOOL cacheOnDisk = !(options & SDWebImageCacheMemoryOnly);
if (options & SDWebImageRefreshCached && cachedImage && !downloadedImage) {
// 如果是前面那种情况,就不需要调用completeBlock了,因为前面已经调用了
} else if (downloadedImage && (!downloadedImage.images || (options & SDWebImageTransformAnimatedImage)) && [self.delegate respondsToSelector:@selector(imageManager:transformDownloadedImage:withURL:)]) {
//如果下载成功且 不是GIF图片或者用户设置了即使是GIF也可以转换且 下载的图片是GIF但是用户实现了对图片就行转换的方法 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
UIImage *transformedImage = [self.delegate imageManager:self transformDownloadedImage:downloadedImage withURL:url];
if (transformedImage && finished) {
BOOL imageWasTransformed = ![transformedImage isEqual:downloadedImage];
// 如果转换成功imageData就存一个nil
[self.imageCache storeImage:transformedImage imageData:(imageWasTransformed ? nil : downloadedData) forKey:key toDisk:cacheOnDisk completion:nil];
}
//调用completeBlock把数据传回去
[self callCompletionBlockForOperation:strongOperation completion:completedBlock image:transformedImage data:downloadedData error:nil cacheType:SDImageCacheTypeNone finished:finished url:url];
});
} else {
//下载完成,存储正常图片
if (downloadedImage && finished) {
[self.imageCache storeImage:downloadedImage imageData:downloadedData forKey:key toDisk:cacheOnDisk completion:nil];
}
//即使下载没有完成也需要把数据传过去,也需要把数据传过去以实现渐变效果,但是只需要在完成后再存储
[self callCompletionBlockForOperation:strongOperation completion:completedBlock image:downloadedImage data:downloadedData error:nil cacheType:SDImageCacheTypeNone finished:finished url:url];
}
}
//完成后把operation移除
if (finished) {
[self safelyRemoveOperationFromRunning:strongOperation];
}
}];
//设置operation的取消Block
operation.cancelBlock = ^{
[self.imageDownloader cancel:subOperationToken];
__strong __typeof(weakOperation) strongOperation = weakOperation;
[self safelyRemoveOperationFromRunning:strongOperation];
};
} else if (cachedImage) {
//如果从缓存中找到了image且不需要更新,则直接把数据传过去,注意前面不一样的就是先把数据传了过去再开启下载,下载完成后还要更新缓存,再把数据传一遍
__strong __typeof(weakOperation) strongOperation = weakOperation;
[self callCompletionBlockForOperation:strongOperation completion:completedBlock image:cachedImage data:cachedData error:nil cacheType:cacheType finished:YES url:url];
[self safelyRemoveOperationFromRunning:operation];
} else {
// 如果缓存中没有找到,且用户实现了[self.delegate imageManager:self shouldDownloadImageForURL:url]方法并返回NO
__strong __typeof(weakOperation) strongOperation = weakOperation;
[self callCompletionBlockForOperation:strongOperation completion:completedBlock image:nil data:nil error:nil cacheType:SDImageCacheTypeNone finished:YES url:url];
[self safelyRemoveOperationFromRunning:operation];
}
}];
return operation;
}
//标记2
#ifndef dispatch_main_async_safe
#define dispatch_main_async_safe(block)\
if (strcmp(dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL), dispatch_queue_get_label(dispatch_get_main_queue())) == 0) {\
block();\
} else {\
dispatch_async(dispatch_get_main_queue(), block);\
}
#endif
//这里只是使用了一个宏定义来把代码交到主队列去执行
再看看标记3如何保存的operation
//标记3
- (void)sd_setImageLoadOperation:(nullable id)operation forKey:(nullable NSString *)key {
if (key) {
[self sd_cancelImageLoadOperationWithKey:key];
if (operation) {
SDOperationsDictionary *operationDictionary = [self operationDictionary];
operationDictionary[key] = operation;
}
}
}
- (void)sd_cancelImageLoadOperationWithKey:(nullable NSString *)key {
// Cancel in progress downloader from queue
SDOperationsDictionary *operationDictionary = [self operationDictionary];
id operations = operationDictionary[key];
if (operations) {
if ([operations isKindOfClass:[NSArray class]]) {
for (id <SDWebImageOperation> operation in operations) {
if (operation) {
[operation cancel];
}
}
} else if ([operations conformsToProtocol:@protocol(SDWebImageOperation)]){
[(id<SDWebImageOperation>) operations cancel];
}
[operationDictionary removeObjectForKey:key];
}
}
//这两个方法很简单,就是每新添一个operation都会先看该opertion是否存在,如果存在就先取消并且删除,再重新添加进去
SDImageCache
标记a,就是从缓存中读取了,所以就用到了SDImageCache类,该类是专门负责缓存的
- (nullable NSOperation *)queryCacheOperationForKey:(nullable NSString *)key done:(nullable SDCacheQueryCompletedBlock)doneBlock {
if (!key) {
if (doneBlock) {
doneBlock(nil, nil, SDImageCacheTypeNone);
}
return nil;
}
// 先从内存中查找
UIImage *image = [self imageFromMemoryCacheForKey:key];
if (image) {
NSData *diskData = nil;
if ([image isGIF]) {
//如果是GIF图片,就要去缓存路径中查找,具体查找方式就不展开说了,看看源码很容易理解
diskData = [self diskImageDataBySearchingAllPathsForKey:key];
}
if (doneBlock) {
doneBlock(image, diskData, SDImageCacheTypeMemory);
}
return nil;
}
NSOperation *operation = [NSOperation new];
//在自己创建的队列中异步执行
dispatch_async(self.ioQueue, ^{
if (operation.isCancelled) {
// 如果operation已经取消,就直接返回,不用传值回去
return;
}
//加入自动释放池。因为这些图片数据可能很大,有点占内存,加入自动释放池可以尽早释放这些对象
//以免占用大量内存,因为在子线程中默认是不开启runloop的,所以就没有自动释放池,
//只能等到线程结束时才会释放对象
@autoreleasepool {
NSData *diskData = [self diskImageDataBySearchingAllPathsForKey:key];
UIImage *diskImage = [self diskImageForKey:key];
if (diskImage && self.config.shouldCacheImagesInMemory) {
NSUInteger cost = SDCacheCostForImage(diskImage);
[self.memCache setObject:diskImage forKey:key cost:cost];
}
if (doneBlock) {
//切换回主线程再传值,方便用户直接对UI进行操作
dispatch_async(dispatch_get_main_queue(), ^{
doneBlock(diskImage, diskData, SDImageCacheTypeDisk);
});
}
}
});
return operation;
}
SDWebImageDownloader
标记b,如果缓存没有或者需要更新缓存,这时候就需要开启下载了,也就用到了我们的SDWebImageDownloader类。
- (nullable SDWebImageDownloadToken *)downloadImageWithURL:(nullable NSURL *)url
options:(SDWebImageDownloaderOptions)options
progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
completed:(nullable SDWebImageDownloaderCompletedBlock)completedBlock {
__weak SDWebImageDownloader *wself = self;
//这个方法一进来就调用了另一个方法,并且传了一个返回值为
//SDWebImageDownloaderOperation类型的block过去,那个方法其实就是
//管理operation并创建SDWebImageDownloadToken,具体后面再来分析
//先看看这里block中做了什么吧
return [self addProgressCallback:progressBlock completedBlock:completedBlock forURL:url createCallback:^SDWebImageDownloaderOperation *{
__strong __typeof (wself) sself = wself;
NSTimeInterval timeoutInterval = sself.downloadTimeout;
if (timeoutInterval == 0.0) {
timeoutInterval = 15.0;
}
//创建缓存,并且默认是不进行URLcache的,除非用户设置了SDWebImageDownloaderUseNSURLCache
//因为默认已经开启了图像缓存,就没必要再对请求缓存了
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url cachePolicy:(options & SDWebImageDownloaderUseNSURLCache ? NSURLRequestUseProtocolCachePolicy : NSURLRequestReloadIgnoringLocalCacheData) timeoutInterval:timeoutInterval];
request.HTTPShouldHandleCookies = (options & SDWebImageDownloaderHandleCookies);
管理coockie//是否
request.HTTPShouldUsePipelining = YES; //开启管道下载
if (sself.headersFilter) {
//如果用户设置了请求头就使用用户自定义的否则就是用默认的
request.allHTTPHeaderFields = sself.headersFilter(url, [sself.HTTPHeaders copy]);
}
else {
request.allHTTPHeaderFields = sself.HTTPHeaders;
}
//创建SDWebImageDownloaderOperation
SDWebImageDownloaderOperation *operation = [[sself.operationClass alloc] initWithRequest:request inSession:sself.session options:options];
operation.shouldDecompressImages = sself.shouldDecompressImages;
//设置URL验证
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;
}
[sself.downloadQueue addOperation:operation];
if (sself.executionOrder == SDWebImageDownloaderLIFOExecutionOrder) {
// 如果是后进先出的方式的话要添加依赖,这样可以保证每次都是最后一个添加进来的队列先执行
[sself.lastAddedOperation addDependency:operation];
sself.lastAddedOperation = operation;
}
//可以看出这里主要是在创建一个request并设置各种参数,然后把它保存到operation中传递过去
return operation;
}];
}
好吧,我们继续看看传过来的这个方法干了些什么
- (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;
dispatch_barrier_sync(self.barrierQueue, ^{
//先看看缓存中是否有对应的operation,没有的话就使用前面block返回的
SDWebImageDownloaderOperation *operation = self.URLOperations[url];
if (!operation) {
operation = createCallback();
self.URLOperations[url] = operation;
__weak SDWebImageDownloaderOperation *woperation = operation;
operation.completionBlock = ^{
//设置完成的回调,就是移除这个operation
SDWebImageDownloaderOperation *soperation = woperation;
if (!soperation) return;
if (self.URLOperations[url] == soperation) {
[self.URLOperations removeObjectForKey:url];
};
};
}
//创建SDWebImageDownloadToken
id downloadOperationCancelToken = [operation addHandlersForProgress:progressBlock completed:completedBlock];
token = [SDWebImageDownloadToken new];
token.url = url;
token.downloadOperationCancelToken = downloadOperationCancelToken;
});
return token;
}
好了,走到这里,整个流程都算差不多了。累了吗?
。。。
好吧,我也累了
。。。
休息一下
。。。
但是还没完
。。。。
再休息一下
。。。。
。。。
。。
。
好了,休息够了起来学习吧!!!
你发现没有,这里创建了请求却没有发现请求发送,只是把它添加到了一个自定义的operation中。所以这里又有一个重要的知识点啦,那就是自定义NSOperation,很显然这里的SDWebImageDownloaderOperation就是为了图片下载而量身定制。下面我们就来看看这个SDWebImageDownloaderOperation的实现吧。
- (nonnull instancetype)initWithRequest:(nullable NSURLRequest *)request
inSession:(nullable NSURLSession *)session
options:(SDWebImageDownloaderOptions)options {
if ((self = [super init])) {
_request = [request copy];
_shouldDecompressImages = YES;
_options = options;
_callbackBlocks = [NSMutableArray new];
_executing = NO;
_finished = NO;
_expectedSize = 0;
_unownedSession = session;
responseFromCached = YES; // Initially wrong until `- URLSession:dataTask:willCacheResponse:completionHandler: is called or not called
_barrierQueue = dispatch_queue_create("com.hackemist.SDWebImageDownloaderOperationBarrierQueue", DISPATCH_QUEUE_CONCURRENT);
}
return self;
}
初始化方法其实没什么好讲的,就是设置各种属性,具体每个属性的作用可以看作者注释,就不一一介绍了。
自定义operation最重要的步骤?
- 如果是非并发队列
直接实现main方法,在main方法中执行自定义任务,但是要注意两点:
1.创建释放池
2.正确响应取消事件。 - 并发队列
自定义并发的NSOperation需要以下步骤:
1.start方法:该方法必须实现,
2.main:该方法可选,如果你在start方法中定义了你的任务,则这个方法就可以不实现,但通常为了代码逻辑清晰,通常会在该方法中定 义自己的任务
3.isExecuting isFinished 主要作用是在线程状态改变时,产生适当的KVO通知
4.isAsynchronous(代替之前使用的IsConcurrent) :必须覆盖并返回YES;
好了,废话不多说,直接来看start方法吧
- (void)start {
//如果已经取消了,就重置该队列
@synchronized (self) {
if (self.isCancelled) {
self.finished = YES;
[self reset];
return;
}
//这是作者自定义的宏,就是表示如果是iOS或者tvOS,因为这两个平台都包含UIKit
#if SD_UIKIT
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;
//当应用程序留给后台的时间快要结束时(该时间有限),这个block将执行:
//进行一些清理工作(主线程执行),清理失败会导致程序挂掉
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;
//传一个nil的queue以让session创建一个串行队列
self.ownedSession = [NSURLSession sessionWithConfiguration:sessionConfig
delegate:self
delegateQueue:nil];
session = self.ownedSession;
}
self.dataTask = [session dataTaskWithRequest:self.request];
self.executing = YES; //通知改operation正在执行,注意作者是重写了setter方法的
}
[self.dataTask resume];
//如果任务创建成功,就发送通知否则就把错误传回去
if (self.dataTask) {
for (SDWebImageDownloaderProgressBlock progressBlock in [self callbacksForKey:kProgressCallbackKey]) {
progressBlock(0, NSURLResponseUnknownLength, self.request.URL);
}
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStartNotification object:self];
});
} else {
[self callCompletionBlocksWithError:[NSError errorWithDomain:NSURLErrorDomain code:0 userInfo:@{NSLocalizedDescriptionKey : @"Connection can't be initialized"}]];
}
#if SD_UIKIT
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
}
好了,终于over了,SD的整个流程大概就是这样,但是里面还有很多我没有讲到的代码,还是值得我们去看一看的。
最后,如果有啥错误的地方希望大家指正