欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

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

程序员文章站 2022-11-05 12:55:10
sdwebimage4——框架内部调用简单分析 一、uml图和调用时序图 二 从我们的给uiimageview 设置网络图片开始 首先我们来看看,我们自己调用的方式...

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


一、uml图和调用时序图

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

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


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

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

/*内存+磁盘缓存*/
-(void)download{

    nsstring *path = @"https://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

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

设置占位图片

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

四、downloadimagewithurl

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

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

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

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

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

b. 如果图片没有缓存,那么我们调用 sdwebimagedownloaderdownloadimagewithurl方法创建去下载,这个回调的代码块中处理图片的缓存,回调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;
}

五、downloadimagewithurl

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

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

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

返回操作

//核心方法:下载图片的操作
- (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参考:https://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

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
}