iOS学习笔记-128.SDWebImage4——框架内部调用简单分析

SDWebImage4——框架内部调用简单分析

以下关于源码的注释来自于


文顶顶 的注释。


一、UML图和调用时序图

这里写图片描述

这里写图片描述


二 从我们的给UIImageView 设置网络图片开始

首先我们来看看,我们自己调用的方式

/*内存+磁盘缓存*/
-(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

我们现在进入到上面我们使用的方法中 sd_setImageWithURL

其实这个方法主要做以下事情

  1. 设置占位图片

  2. 如果url不为空,创建一个 SDWebImageOperation的操作,并且添加到图片加载的操作缓存中

    创建操作的时候 调用了 SDWebImageManager.sharedManager downloadImageWithURL,获取结束后,

    根据有没有图片和我们之前设置的状态,来判断调用 completedBlock

  3. 如果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 <SDWebImageOperation> 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);
            }
        });
    }
}

四、downloadImageWithURL

  1. 对url进行判断(是否合法,是否在黑名单里面)

  2. 如果url不正确或者 选择的下载策略不是『下载失败尝试重新下载』且该URL存在于黑名单中,那么直接返回,回调任务完成block块,传递错误信息

  3. 把当前的下载任务添加到『当前正在执行任务数组』中

  4. 查找URLKEY对应的图片缓存是否存在,查找完毕之后把该图片(存在|不存在)和该图片的缓存方法以block的方式传递,
    缓存情况查找完毕之后,在block块中进行后续处理。

    a. 操作取消,从『当前正在执行任务数组』移除我们的 3 步骤中添加的操作,不在执行block的后续操作

    b. 如果图片没有缓存,那么我们调用 SDWebImageDownloaderdownloadImageWithURL方法创建去下载,这个回调的代码块中处理图片的缓存,回调block,从『当前正在执行任务数组』移除我们的 3 步骤中添加的操作

    c. 如果缓存图片存在,那么回调block,从『当前正在执行任务数组』移除我们的 3 步骤中添加的操作

    d. 图片不存在缓存且不允许代理下载,那么在主线程中回调completedBlock,从『当前正在执行任务数组』移除我们的 3 步骤中添加的操作

  5. 返回创建的操作。

- (id <SDWebImageOperation>)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 <SDWebImageOperation> 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;
}

五、downloadImageWithURL

  1. 处理下载超时,如果没有设置过则初始化为15秒

  2. 创建一个 NSMutableURLRequest 的请求,但是现在还不去操作

  3. 调用 SDWebImageDownloaderOperationinitWithRequest创建下载图片的操作。下载一部分回调一下我们的进度的方法。下载完成后进行一系列的判断然后调用回调。

  4. 返回操作

//核心方法:下载图片的操作
- (id <SDWebImageOperation>)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

  1. initWithRequest 中会初始化一些操作

  2. 我们知道 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
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值