以下关于源码的注释来自于
文顶顶 的注释。
首先我们来看看,我们自己调用的方式
/*内存+磁盘缓存*/
-(void)download{
NSString *path = @"http://www.qqpk.cn/Article/UploadFiles/201112/20111208140318990.jpg";
[self.imageView sd_setImageWithURL:[NSURL URLWithString:path] placeholderImage:[UIImage imageNamed:@"image1"] options:0 progress:^(NSInteger receivedSize, NSInteger expectedSize) {
} completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) {
}];
}
我们现在进入到上面我们使用的方法中 sd_setImageWithURL
其实这个方法主要做以下事情
设置占位图片
如果url不为空,创建一个 SDWebImageOperation的操作,并且添加到图片加载的操作缓存中
创建操作的时候 调用了 SDWebImageManager.sharedManager downloadImageWithURL,获取结束后,
根据有没有图片和我们之前设置的状态,来判断调用 completedBlock
如果url为空,那么回调到传递过来的completedBlock代码块上 ,把 image 设为nil
//下载图片的核心方法
/*
* url 图片的二进制数据
* placeholder UIImageView的占位图片
* options 图片下载选项(策略)
* progressBlock 进度回调
* completedBlock 完成回调
*/
- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletionBlock)completedBlock {
// 取消当前图像下载
[self sd_cancelCurrentImageLoad];
// 利用运行时retain url
objc_setAssociatedObject(self, &imageURLKey, url, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
//判断,如果传入的下载策略不是延迟显示占位图片,那么在主线程中设置占位图片
if (!(options & SDWebImageDelayPlaceholder)) {
dispatch_main_async_safe(^{
// 设置占位图像
self.image = placeholder;
});
}
//如果url不为空
if (url) {
// check if activityView is enabled or not
//检查activityView是否可用
if ([self showActivityIndicatorView]) {
[self addActivityIndicator];
}
__weak __typeof(self)wself = self;
// 实例化 SDWebImageOperation 操作
id operation = [SDWebImageManager.sharedManager downloadImageWithURL:url options:options progress:progressBlock completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
//移除UIActivityIndicatorView
[wself removeActivityIndicator];
if (!wself) return;
//下面block中的操作在主线程中处理
dispatch_main_sync_safe(^{
if (!wself) return;
//如果图片下载完成,且传入的下载选项为手动设置图片则直接执行completedBlock回调,并返回
if (image && (options & SDWebImageAvoidAutoSetImage) && completedBlock)
{
completedBlock(image, error, cacheType, url);
return;
}
else if (image) { //否则,如果图片存在,则设置图片到UIImageView上面,并刷新重绘视图
wself.image = image;
[wself setNeedsLayout];
} else {
//如果没有得到图像
//如果传入的下载选项为延迟显示占位图片,则设置占位图片到UIImageView上面,并刷新重绘视图
if ((options & SDWebImageDelayPlaceholder)) {
wself.image = placeholder;
[wself setNeedsLayout];
}
}
if (completedBlock && finished) {
completedBlock(image, error, cacheType, url);
}
});
}];
[self sd_setImageLoadOperation:operation forKey:@"UIImageViewImageLoad"];
} else {
//如果url为空,则在主线中处理下面的操作
dispatch_main_async_safe(^{
//移除UIActivityIndicatorView
[self removeActivityIndicator];
//处理错误信息,并执行任务结束回调,把错误信息作为参数传递出去
NSError *error = [NSError errorWithDomain:SDWebImageErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey : @"Trying to load a nil url"}];
if (completedBlock) {
completedBlock(nil, error, SDImageCacheTypeNone, url);
}
});
}
}
对url进行判断(是否合法,是否在黑名单里面)
如果url不正确或者 选择的下载策略不是『下载失败尝试重新下载』且该URL存在于黑名单中,那么直接返回,回调任务完成block块,传递错误信息
把当前的下载任务添加到『当前正在执行任务数组』中
查找URLKEY对应的图片缓存是否存在,查找完毕之后把该图片(存在|不存在)和该图片的缓存方法以block的方式传递,
缓存情况查找完毕之后,在block块中进行后续处理。
a. 操作取消,从『当前正在执行任务数组』移除我们的 3 步骤中添加的操作,不在执行block的后续操作
b. 如果图片没有缓存,那么我们调用 SDWebImageDownloader的
downloadImageWithURL
方法创建去下载,这个回调的代码块中处理图片的缓存,回调block,从『当前正在执行任务数组』移除我们的 3 步骤中添加的操作c. 如果缓存图片存在,那么回调block,从『当前正在执行任务数组』移除我们的 3 步骤中添加的操作
d. 图片不存在缓存且不允许代理下载,那么在主线程中回调completedBlock,从『当前正在执行任务数组』移除我们的 3 步骤中添加的操作
返回创建的操作。
- (id )downloadImageWithURL:(NSURL *)url
options:(SDWebImageOptions)options
progress:(SDWebImageDownloaderProgressBlock)progressBlock
completed:(SDWebImageCompletionWithFinishedBlock)completedBlock {
// Invoking this method without a completedBlock is pointless
//没有completedblock,那么调用这个方法是毫无意义的
NSAssert(completedBlock != nil, @"If you mean to prefetch the image, use -[SDWebImagePrefetcher prefetchURLs] instead");
// Very common mistake is to send the URL using NSString object instead of NSURL. For some strange reason, XCode won't
// throw any warning for this type mismatch. Here we failsafe this error by allowing URLs to be passed as NSString.
//检查用户传入的URL是否正确,如果该URL是NSString类型的,那么尝试转换
if ([url isKindOfClass:NSString.class]) {
url = [NSURL URLWithString:(NSString *)url];
}
// Prevents app crashing on argument type error like sending NSNull instead of NSURL
//防止因参数类型错误而导致应用程序崩溃,判断URL是否是NSURL类型的,如果不是则直接设置为nil
if (![url isKindOfClass:NSURL.class]) {
url = nil;
}
//初始化一个SDWebImageCombinedOperationBlock块
__block SDWebImageCombinedOperation *operation = [SDWebImageCombinedOperation new];
__weak SDWebImageCombinedOperation *weakOperation = operation;
BOOL isFailedUrl = NO; //初始化设定该URL是正确的
//加互斥锁,检索请求图片的URL是否在曾下载失败的集合中(URL黑名单)
@synchronized (self.failedURLs) {
isFailedUrl = [self.failedURLs containsObject:url];
}
//如果url不正确或者 选择的下载策略不是『下载失败尝试重新下载』且该URL存在于黑名单中,那么直接返回,回调任务完成block块,传递错误信息
if (url.absoluteString.length == 0 || (!(options & SDWebImageRetryFailed) && isFailedUrl)) {
//该宏保证了completedBlock回调在主线程中执行
dispatch_main_sync_safe(^{
NSError *error = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorFileDoesNotExist userInfo:nil];
completedBlock(nil, error, SDImageCacheTypeNone, YES, url);
});
return operation;
}
//加互斥锁,把当前的下载任务添加到『当前正在执行任务数组』中
@synchronized (self.runningOperations) {
[self.runningOperations addObject:operation];
}
//得到该URL对应的缓存KEY
NSString *key = [self cacheKeyForURL:url];
//该方法查找URLKEY对应的图片缓存是否存在,查找完毕之后把该图片(存在|不存在)和该图片的缓存方法以block的方式传递
//缓存情况查找完毕之后,在block块中进行后续处理(如果该图片没有缓存·下载|如果缓存存在|如果用户设置了下载的缓存策略是刷新缓存如何处理等等)
operation.cacheOperation = [self.imageCache queryDiskCacheForKey:key done:^(UIImage *image, SDImageCacheType cacheType) {
//先判断该下载操作是否已经被取消,如果被取消则把当前操作从runningOperations数组中移除,并直接返回
if (operation.isCancelled) {
@synchronized (self.runningOperations) {
[self.runningOperations removeObject:operation];
}
return;
}
//(图片不存在||下载策略为刷新缓存)且(shouldDownloadImageForURL不能响应||该图片存在缓存)
if ((!image || options & SDWebImageRefreshCached) && (![self.delegate respondsToSelector:@selector(imageManager:shouldDownloadImageForURL:)] || [self.delegate imageManager:self shouldDownloadImageForURL:url])) {
//从此处开始,一直在处理downloaderOptions(即下载策略)
if (image && options & SDWebImageRefreshCached) { //如果图像存在,但是下载策略为刷新缓存,则通知缓存图像并尝试重新下载
dispatch_main_sync_safe(^{
// If image was found in the cache but SDWebImageRefreshCached is provided, notify about the cached image
// AND try to re-download it in order to let a chance to NSURLCache to refresh it from server.
completedBlock(image, nil, cacheType, YES, url);
});
}
// download if no image or requested to refresh anyway, and download allowed by delegate
SDWebImageDownloaderOptions downloaderOptions = 0;
//如果下载策略为SDWebImageLowPriority 那么downloaderOptions = 其本身
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 (image && 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
//忽略从NSURLCache读取图片
downloaderOptions |= SDWebImageDownloaderIgnoreCachedResponse;
}
//到此处位置,downloaderOptions(即下载策略)处理操作结束
//核心方法:使用下载器,下载图片
id subOperation = [self.imageDownloader downloadImageWithURL:url options:downloaderOptions progress:progressBlock completed:^(UIImage *downloadedImage, NSData *data, NSError *error, BOOL finished) {
if (weakOperation.isCancelled) {
//如果此时操作被取消,那么什么也不做
// Do nothing if the operation was cancelled
// See #699 for more details
// if we would call the completedBlock, there could be a race condition between this block and another completedBlock for the same object, so if this one is called second, we will overwrite the new data
}
else if (error) { //如果下载失败,则处理结束的回调,在合适的情况下把对应图片的URL添加到黑名单中
dispatch_main_sync_safe(^{
if (!weakOperation.isCancelled) {
completedBlock(nil, error, SDImageCacheTypeNone, finished, 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) {
[self.failedURLs addObject:url];
}
}
}
else {//下载成功
//先判断当前的下载策略是否是SDWebImageRetryFailed,如果是那么把该URL从黑名单中删除
if ((options & SDWebImageRetryFailed)) {
@synchronized (self.failedURLs) {
[self.failedURLs removeObject:url];
}
}
//是否要进行磁盘缓存?
BOOL cacheOnDisk = !(options & SDWebImageCacheMemoryOnly);
//如果下载策略为SDWebImageRefreshCached且该图片缓存中存在且未下载下来,那么什么都不做
if (options & SDWebImageRefreshCached && image && !downloadedImage) {
// Image refresh hit the NSURLCache cache, do not call the completion block
}
else if (downloadedImage && (!downloadedImage.images || (options & SDWebImageTransformAnimatedImage)) && [self.delegate respondsToSelector:@selector(imageManager:transformDownloadedImage:withURL:)]) {
//否则,如果下载图片存在且(不是可动画图片数组||下载策略为SDWebImageTransformAnimatedImage&&transformDownloadedImage方法可用)
//开子线程处理
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
//在下载后立即将图像转换,并进行磁盘和内存缓存
UIImage *transformedImage = [self.delegate imageManager:self transformDownloadedImage:downloadedImage withURL:url];
#warning 2
if (transformedImage && finished) {
BOOL imageWasTransformed = ![transformedImage isEqual:downloadedImage];
[self.imageCache storeImage:transformedImage recalculateFromImage:imageWasTransformed imageData:(imageWasTransformed ? nil : data) forKey:key toDisk:cacheOnDisk];
}
//在主线程中回调completedBlock
dispatch_main_sync_safe(^{
if (!weakOperation.isCancelled) {
completedBlock(transformedImage, nil, SDImageCacheTypeNone, finished, url);
}
});
});
}
else {
//得到下载的图片且已经完成,则进行缓存处理
if (downloadedImage && finished) {
[self.imageCache storeImage:downloadedImage recalculateFromImage:NO imageData:data forKey:key toDisk:cacheOnDisk];
}
dispatch_main_sync_safe(^{
if (!weakOperation.isCancelled) {
completedBlock(downloadedImage, nil, SDImageCacheTypeNone, finished, url);
}
});
}
}
if (finished) {
@synchronized (self.runningOperations) {
[self.runningOperations removeObject:operation];
}
}
}];
//处理cancelBlock
operation.cancelBlock = ^{
[subOperation cancel];
@synchronized (self.runningOperations) {
[self.runningOperations removeObject:weakOperation];
}
};
}
else if (image) { //如果图片存在,且操作没有被取消,那么在主线程中回调completedBlock,并把当前操作移除
dispatch_main_sync_safe(^{
if (!weakOperation.isCancelled) {
completedBlock(image, nil, cacheType, YES, url);
}
});
@synchronized (self.runningOperations) {
[self.runningOperations removeObject:operation];
}
}
else {
// Image not in cache and download disallowed by delegate
//图片不存在缓存且不允许代理下载,那么在主线程中回调completedBlock,并把当前操作移除
dispatch_main_sync_safe(^{
if (!weakOperation.isCancelled) {
completedBlock(nil, nil, SDImageCacheTypeNone, YES, url);
}
});
@synchronized (self.runningOperations) {
[self.runningOperations removeObject:operation];
}
}
}];
return operation;
}
处理下载超时,如果没有设置过则初始化为15秒
创建一个 NSMutableURLRequest 的请求,但是现在还不去操作
调用 SDWebImageDownloaderOperation的initWithRequest
创建下载图片的操作。下载一部分回调一下我们的进度的方法。下载完成后进行一系列的判断然后调用回调。
返回操作
//核心方法:下载图片的操作
- (id )downloadImageWithURL:(NSURL *)url options:(SDWebImageDownloaderOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageDownloaderCompletedBlock)completedBlock {
__block SDWebImageDownloaderOperation *operation;
__weak __typeof(self)wself = self; //为了避免block的循环引用
//处理进度回调|完成回调等,如果该url在self.URLCallbacks并不存在,则调用createCallback block块
[self addProgressCallback:progressBlock completedBlock:completedBlock forURL:url createCallback:^{
//处理下载超时,如果没有设置过则初始化为15秒
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
//根据给定的URL和缓存策略创建可变的请求对象,设置请求超时
//请求策略:如果是SDWebImageDownloaderUseNSURLCache则使用NSURLRequestUseProtocolCachePolicy,否则使用NSURLRequestReloadIgnoringLocalCacheData
/*
NSURLRequestUseProtocolCachePolicy:默认的缓存策略
1)如果缓存不存在,直接从服务端获取。
2)如果缓存存在,会根据response中的Cache-Control字段判断下一步操作,如: Cache-Control字段为must-revalidata, 则询问服务端该数据是否有更新,无更新的话直接返回给用户缓存数据,若已更新,则请求服务端.
NSURLRequestReloadIgnoringLocalCacheData:忽略本地缓存数据,直接请求服务端。
*/
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url cachePolicy:(options & SDWebImageDownloaderUseNSURLCache ? NSURLRequestUseProtocolCachePolicy : NSURLRequestReloadIgnoringLocalCacheData) timeoutInterval:timeoutInterval];
//设置是否使用Cookies(采用按位与)
/*
关于cookies参考:http://blog.csdn.net/chun799/article/details/17206907
*/
request.HTTPShouldHandleCookies = (options & SDWebImageDownloaderHandleCookies);
//开启HTTP管道,这可以显著降低请求的加载时间,但是由于没有被服务器广泛支持,默认是禁用的
request.HTTPShouldUsePipelining = YES;
//设置请求头信息(过滤等)
if (wself.headersFilter) {
request.allHTTPHeaderFields = wself.headersFilter(url, [wself.HTTPHeaders copy]);
}
else {
request.allHTTPHeaderFields = wself.HTTPHeaders;
}
//核心方法:创建下载图片的操作
operation = [[wself.operationClass alloc] initWithRequest:request
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];
});
//遍历callbacksForURL数组中的所有字典,执行SDWebImageDownloaderProgressBlock回调
for (NSDictionary *callbacks in callbacksForURL) {
//说明:SDWebImageDownloaderProgressBlock作者可能考虑到用户拿到进度数据后会进行刷新处理,因此在主线程中处理了回调
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];
//如果完成,那么把URL从URLCallbacks字典中删除
if (finished) {
[sself.URLCallbacks removeObjectForKey:url];
}
});
//遍历callbacksForURL数组中的所有字典,执行SDWebImageDownloaderCompletedBlock回调
for (NSDictionary *callbacks in callbacksForURL) {
SDWebImageDownloaderCompletedBlock callback = callbacks[kCompletedCallbackKey];
if (callback) callback(image, data, error, finished);
}
}
cancelled:^{
SDWebImageDownloader *sself = wself;
if (!sself) return;
//把当前的url从URLCallbacks字典中移除
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) {
//设置 https 访问时身份验证使用的凭据
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内部的start方法开启图片的下载任务
[wself.downloadQueue addOperation:operation];
//判断任务的执行优先级,如果是后进先出,则调整任务的依赖关系,优先执行当前的(最后添加)任务
if (wself.executionOrder == SDWebImageDownloaderLIFOExecutionOrder) {
// Emulate LIFO execution order by systematically adding new operations as last operation's dependency
[wself.lastAddedOperation addDependency:operation];
wself.lastAddedOperation = operation;//设置当前下载操作为最后一个操作
}
}];
return operation;
}
initWithRequest 中会初始化一些操作
我们知道 NSOperation 内部会调用start方法。这里面的下载操作放到了start中
//初始化operation的方法
- (id)initWithRequest:(NSURLRequest *)request
options:(SDWebImageDownloaderOptions)options
progress:(SDWebImageDownloaderProgressBlock)progressBlock
completed:(SDWebImageDownloaderCompletedBlock)completedBlock
cancelled:(SDWebImageNoParamsBlock)cancelBlock {
if ((self = [super init])) {
_request = request;
_shouldDecompressImages = YES;
_shouldUseCredentialStorage = YES;
_options = options;
_progressBlock = [progressBlock copy];
_completedBlock = [completedBlock copy];
_cancelBlock = [cancelBlock copy];
_executing = NO;
_finished = NO;
_expectedSize = 0;
responseFromCached = YES; // Initially wrong until `connection:willCacheResponse:` is called or not called
}
return self;
}
//核心方法:在该方法中处理图片下载操作
- (void)start {
@synchronized (self) {
//判断当前操作是否被取消,如果被取消了,则标记任务结束,并处理后续的block和清理操作
if (self.isCancelled) {
self.finished = YES;
[self reset];
return;
}
//条件编译,如果是iphone设备且大于4.0
#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单例对象
UIApplication * app = [UIApplicationClass performSelector:@selector(sharedApplication)];
//UIBackgroundTaskIdentifier:通过UIBackgroundTaskIdentifier可以实现有限时间内在后台运行程序
//在后台获取一定的时间去指行我们的代码
self.backgroundTaskId = [app beginBackgroundTaskWithExpirationHandler:^{
__strong __typeof (wself) sself = wself;
#warning 3
if (sself) {
[sself cancel]; //取消当前下载操作
[app endBackgroundTask:sself.backgroundTaskId]; //结束后台任务
sself.backgroundTaskId = UIBackgroundTaskInvalid;
}
}];
}
#endif
self.executing = YES; //当前任务正在执行
//创建NSURLConnection对象,并设置代理(没有马上发送请求)
self.connection = [[NSURLConnection alloc] initWithRequest:self.request delegate:self startImmediately:NO];
//获得当前线程
self.thread = [NSThread currentThread];
}
[self.connection start]; //发送网络请求
if (self.connection) {
if (self.progressBlock) {
//进度block的回调
self.progressBlock(0, NSURLResponseUnknownLength);
}
//注册通知中心,在主线程中发送通知SDWebImageDownloadStartNotification【任务开始下载】
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStartNotification object:self];
});
//开启线程对应的Runloop
if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_5_1) {
// Make sure to run the runloop in our background thread so it can process downloaded data
//确保后台线程的runloop跑起来
// Note: we use a timeout to work around an issue with NSURLConnection cancel under iOS 5
// not waking up the runloop, leading to dead threads (see https://github.com/rs/SDWebImage/issues/466)
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 10, false);
}
else {
//开启Runloop
CFRunLoopRun();
}
if (!self.isFinished) {
[self.connection cancel]; //取消网络连接
//处理错误信息
[self connection:self.connection didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorTimedOut userInfo:@{NSURLErrorFailingURLErrorKey : self.request.URL}]];
}
}
else {
//执行completedBlock回调,打印Connection初始化失败
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
}