SDWebImage源码解读

SDWebImage是一个优秀之开源之老三方库,它抱有以下功能:
供UIImageView的一个分拣,以支持网络图片的加载与缓存管理

六、数据解析工具

1.百度统计

斯是境内站点必然要因此之站点统计工具,跟踪站点相关数据。

2.Google
Analytics

谷歌分析工具(GA),特别是面向海外SEO的站点,这个邪是必用工具哦。

3.office三剑客——word、excel、PowerPoint

也许产生若干小伙伴看这仨上未了桌面,这不是太中心的啊。而实际刚就是是这些类似接触时最好丰富太不起眼的软件,很多营业人员还开不了,仅留于表面。特别是Excel,基本的多少透视表制作、每月每年的表格制作还见面为此到。

  1. 一个异步的图形加载器
  2. 一个异步的内存+磁盘图片缓存
  3. 支持GIF图片
  4. 支持WebP图片
  5. 后台图片解压缩处理
  6. 确保与一个URL的图未吃下载多次
  7. 担保虚假的URL不会见于频繁加载
  8. 包下载和缓存时,主线程不深受卡住

季、原型设计工具

Axure

Axure是一个专业的高速原型设计工具,让负责定义需求和准星、设计功能与界面的学者会迅速创建以软件要Web网站的线框图、流程图、原型和标准说明文档。不过Axure通常产品经理用的比多,作为运营,我平常是以举行产品分析优化方案还是做图时会见为此到其。

下我们即便因我们平常之动啊进口,来对SDWebImage的源码进行剖析。

小结

本文和大家共同享用运营工作吃常用之工具软件,如果大家还发出其他的好工具,欢迎并享用交流。

相似我们采用SDWebImage用得最好多之即是以此函数
- (void)sd_setImageWithURL:(nullable NSURL *)url placeholderImage:(nullable UIImage *)placeholder options:(SDWebImageOptions)options

俗话说“欲善其功
必先利其器”,特别是身为运营工作人口对这一定还深有感触。运营工作涉及面较常见,事情基本上设掺杂,所以把现有的营业相关工具充分利用起来,往往得事半功倍,且从至赞助作用。本文跟大家大饱眼福下自己个人平时营业工作面临不时因此到之家伙应用,大家可互补分享哦。废话不多说,上干货~

下面我们就是打之函数入口开始同步一步分析。
翻开源码可以发现,最终函数掉于是了
其一函数

老三、思维导图工具

1.freemind

首推freemind是盖她是完全免费的。虽然界面效果上可能没有后面要引进的花哨好看,但功效上基本能够满足我们的常用需求了。

2.Xmind

XMind以该好之易用性,友好的界面与强硬的效能吸引了许多用户。

3.百度脑图

百度脑图也是如出一辙暂缓便捷的脑图编辑工具,操作简易。支持在线一直创造、保存并享受您的笔触。免安装云存储易分享心得舒适功能丰富.。

SDWebImage的着力类就是三只:

仲、美工设计、图片资源、处理、管理工具

1.昵图网

昵图网是同等贱著名的图片素材共享平台,网站及之持有材料图片均出于网友上传共享的,你上传共享的图片越多,你能下载的图纸为就一发多。当然也发生免费及付费的。

2.千库网

本仓房网是境内首家顾提供免抠PNG图片的资料网站。免费生载有限制,更多生充斥需要付费哦~

方两下虽然有付费服务,但价格达成还算是能接受。下面分享两只免费、无版权的图资源下载站。

3.pixabay

Pixabay约有910000摆放免费之相片、矢量文件及道插图。图片覆盖面广,文件呢闹许多尺寸可以选取。图片质量高,小编首推向的图片素材网站。里面的图纸可以复制、修改、转发等方法下,甚至当商业用途,无需申请获准,也随便需支付版税。

4.SplitShire

SplitShire网站的图片质量为生高,大都比较feel风。

5.photoshop

其一就算不需自家了多介绍了,众所周知的图样处理软件。

6.AI

可能部分小伙伴并无常用AI,多为此PS。这个看现实要求吧,通常矢量图处理用AI,像素图处理用PS。平时做运动海报、宣传图等一般二者匹配使用。

7.Ulead GIF
Animator

UGA是用来制作、处理gif动态图的,有时需要在软文中插入个为笑有趣的gif动态图己不怕就此其。

8.Eagle

Eagle是一个业内的图样管理软件,更优雅的整理图片素材与统筹灵感,试用+付费的,不过价格还算可以。这个特别符合设前端设计师等常和图片打交道的童鞋,作为运营谈不上不可或缺,感兴趣之可试试。

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是否存在,如果存在就先取消并且删除,再重新添加进去

五、待处、任务管理工具

然的同

实际就本来是相同慢企业办公室一起软件,我个人因此它来举行时间、待处、任务管理。因为运营工作业多且杂,不记待办可能回头就记不清了。你还在于是好贴?!太OUT了~

高效Todo

夫软件的界面设计都是以直观的季象限设计,这样的筹划好叫用户一目了然的了解如何工作是十万火急需要处理的。

options参数是独NS_OPTIONS类型,其中起12单参数,你可因你的花色需求进行分选,具体每个参数的意我哪怕不一一解释了,因为作者注已经说得要命了解。

同等、内容排版工具

1.i 排版

微信公众号图文信息排版工具,支持各种富文本样式格式

2.秀米

秀米是一样舒缓基于微信公众平台的图文编辑和排版工具,它比微信自带的编辑器多了不少排版功能跟美化工具。在秀米上将内容编排排版之后,直接复制粘贴到大众号后台即可。

3.壹伴公众号有点插件

本着群众号后台进行加强,实现一键传图、公众号内排版、本地传图等。

  • SDImageCache
    其要负责图片的缓存

  • SDWebImageDownloader
    它们根本承担图片的下载

  • SDWebImageManager
    其要承担图片的下载操作的军事管制。而且我们常采取的诸如UIImageView+WebCache等控件的归类且是基于SDWebImageManager对象的。该目标将一个下载器和一个图纸缓存绑定在齐,并对外提供零星只只念属性来收获她

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的整个流程大概就是这般,但是其中还有好多我没讲到的代码,还是值得我们错过看无异拘禁之。
末尾,如果发生何错误的地方想大家指正

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

Post Author: admin

发表评论

电子邮件地址不会被公开。 必填项已用*标注