iOS学习----------SDWebImage框架解析(2)

来源:互联网 发布:添加网络监视器 编辑:程序博客网 时间:2024/05/16 10:45

2. operation执行过程

- (id <SDWebImageOperation>)downloadImageWithURL:(NSURL *)url

                                         options:(SDWebImageOptions)options

                                        progress:(SDWebImageDownloaderProgressBlock)progressBlock

                                       completed:(SDWebImageCompletionWithFinishedBlock)completedBlock 

/*

 *     url:下载图片的url

 *   option:该请求所要遵循的选项

 *   progressBlock:图片正在下载调用的block

 *   completedBlock:当操作完成后调用该block这个参数必须存在

 *

 *   block没有返回值并且用请求的UIImage作为第一个参数。

 *   如果请求出错,那么image参数为nil,而第二参数将包含一个NSError对象。

 *   typedef void(^SDWebImageCompletionWithFinishedBlock)(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL);

     SDImageCacheType:图片获取的途径:硬盘、内存、web

 *   options设为SDWebImageProgressiveDownload并且此时图片正在下载,finished将设为NO

 *   因此这个block会不停地调用直到图片下载完成,此时才会设置finishedYES.

 *

 *   return:返回一个遵循SDWebImageOperation协议的NSObject.应该是一个SDWebImageDownloaderOperation的实例

 */


- (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类型为string 那么要转变成NSURL类型

    if ([urlisKindOfClass:NSString.class]) {

        url = [NSURL URLWithString:(NSString *)url];

    }

防止传入一个NSNull给

    // Prevents app crashing on argument type error like sending NSNull instead of NSURL

    if (![urlisKindOfClass:NSURL.class]) {

        url = nil;

    }


创建operation

    __blockSDWebImageCombinedOperation *operation = [SDWebImageCombinedOperationnew];

    __weak SDWebImageCombinedOperation *weakOperation = operation;


搜索当前的url是否在failedUrs的集合中,这个failedURLs保存

    BOOL isFailedUrl = NO;

    @synchronized (self.failedURLs) {

        isFailedUrl = [self.failedURLscontainsObject:url];

    }

如果这个图片url无法下载,那就使用completedBlock进行错误处,图片无法下载的情况有两种:

第一种情况是该url为空,另一种情况就是如果是failedUrl也无法下载,但是要避免无法下载就放入failedUrl的情况,就要设置options为SDWebImageRetryFailed。一般默认image无法下载,这个url就会加入黑名单,但是设置了SDWebImageRetryFailed会禁止添加到黑名单,不停重新下载。

    if (url.absoluteString.length ==0 || (!(options &SDWebImageRetryFailed) && isFailedUrl)) {

        dispatch_main_sync_safe(^{ 

主线程中进行错误处理

            NSError *error = [NSErrorerrorWithDomain:NSURLErrorDomaincode:NSURLErrorFileDoesNotExistuserInfo:nil];

            completedBlock(nil, error, SDImageCacheTypeNone, YES, url);

        });

        return operation;

    }

如果可以下载就将这个operation加入到数组中(加锁操作)

    @synchronized (self.runningOperations) {

        [self.runningOperationsaddObject:operation];

    }

用图片的url来获取cache对应的key,也就是说cache中如果已经有了该图片,那就返回该图片在cache中对应的key,你可以根据这个key去cache中获取图片

    NSString *key = [selfcacheKeyForURL:url];


获取到key通过key来

    operation.cacheOperation = [self.imageCachequeryDiskCacheForKey:keydone:^(UIImage *image,SDImageCacheType cacheType) {

        if (operation.isCancelled) {

            @synchronized (self.runningOperations) {

                [self.runningOperationsremoveObject:operation];

            }


            return;

        }

如果delegate没有实现上面那个函数,整个表达式就为真,相当于该函数返回了YES。如果delegate实现了该函数,那就执行该函数,并且判断该函数执行结果。如果函数返回NO,那么整个if表达式都为NO,那么当图片缓存未命中时,图片下载反而被阻止。后半段目前恒等于YES

        if ((!image || options & SDWebImageRefreshCached) && (![self.delegaterespondsToSelector:@selector(imageManager:shouldDownloadImageForURL:)] || [self.delegateimageManager:selfshouldDownloadImageForURL:url])) {

如果图片在缓存中找到,但是options中有SDWebImageRefreshCached

那么就尝试重新下载该图片,这样是NSURLCache有机会从服务器端刷新自身缓存。

            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;

            

            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

                downloaderOptions |= SDWebImageDownloaderIgnoreCachedResponse;

            }

            

            

            id <SDWebImageOperation> subOperation = [self.imageDownloaderdownloadImageWithURL:urloptions:downloaderOptions progress:progressBlockcompleted:^(UIImage *downloadedImage,NSData *data,NSError *error,BOOL finished) {

                __strong __typeof(weakOperation) strongOperation = weakOperation;

                if (!strongOperation || strongOperation.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) {

                    dispatch_main_sync_safe(^{

                        if (strongOperation && !strongOperation.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.failedURLsaddObject:url];

                        }

                    }

                }

                else {

                    if ((options & SDWebImageRetryFailed)) {

                        @synchronized (self.failedURLs) {

                            [self.failedURLsremoveObject:url];

                        }

                    }

                    

                    BOOL cacheOnDisk = !(options &SDWebImageCacheMemoryOnly);


                    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.delegaterespondsToSelector:@selector(imageManager:transformDownloadedImage:withURL:)]) {

                        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH,0), ^{

                            UIImage *transformedImage = [self.delegateimageManager:selftransformDownloadedImage:downloadedImagewithURL:url];


                            if (transformedImage && finished) {

                                BOOL imageWasTransformed = ![transformedImageisEqual:downloadedImage];

                                [self.imageCachestoreImage:transformedImagerecalculateFromImage:imageWasTransformed imageData:(imageWasTransformed ?nil : data)forKey:keytoDisk:cacheOnDisk];

                            }


                            dispatch_main_sync_safe(^{

                                if (strongOperation && !strongOperation.isCancelled) {

                                    completedBlock(transformedImage, nil, SDImageCacheTypeNone, finished, url);

                                }

                            });

                        });

                    }

                    else {

                        if (downloadedImage && finished) {

                            [self.imageCachestoreImage:downloadedImagerecalculateFromImage:NOimageData:data forKey:keytoDisk:cacheOnDisk];

                        }


                        dispatch_main_sync_safe(^{

                            if (strongOperation && !strongOperation.isCancelled) {

                                completedBlock(downloadedImage, nil, SDImageCacheTypeNone, finished, url);

                            }

                        });

                    }

                }


                if (finished) {

                    @synchronized (self.runningOperations) {

                        if (strongOperation) {

                            [self.runningOperationsremoveObject:strongOperation];

                        }

                    }

                }

            }];

            operation.cancelBlock = ^{

                [subOperation cancel];

                

                @synchronized (self.runningOperations) {

                    __strong __typeof(weakOperation) strongOperation = weakOperation;

                    if (strongOperation) {

                        [self.runningOperationsremoveObject:strongOperation];

                    }

                }

            };

        }

       elseif (image) {

从缓存中获取到了图片,而且不需要刷新缓存的  

直接执行completedBlock,其中error置为nil即可。

            dispatch_main_sync_safe(^{

                __strong __typeof(weakOperation) strongOperation = weakOperation;

                if (strongOperation && !strongOperation.isCancelled) {

                    completedBlock(image, nil, cacheType,YES, url);

                }

            });

执行完后,说明图片获取成功,可以把当前这个operation溢移除了。

            @synchronized (self.runningOperations) {

                [self.runningOperationsremoveObject:operation];

            }

        }

        else {

            // Image not in cache and download disallowed by delegate


又没有从缓存中获取到图片,shouldDownloadImageForURL又返回NO,不允许下载

所以completedBlock中image和error均传入nil

            dispatch_main_sync_safe(^{

                __strong __typeof(weakOperation) strongOperation = weakOperation;

                if (strongOperation && !weakOperation.isCancelled) {

                    completedBlock(nil, nil, SDImageCacheTypeNone, YES, url);

                }

            });

            @synchronized (self.runningOperations) {

                [self.runningOperationsremoveObject:operation];

            }

        }

    }];


    return operation;

}



**********************************************************

- (NSOperation *)queryDiskCacheForKey:(NSString *)key done:(SDWebImageQueryCompletedBlock)doneBlock {

doneBlock必须实现

    if (!doneBlock) {

        return nil;

    }

如果key为nil,则在内存中没有图片,则之间返并标记为web模式

    if (!key) {

        doneBlock(nil, SDImageCacheTypeNone);

        return nil;

    }


    // First check the in-memory cache...

从内存中取图片

    UIImage *image = [selfimageFromMemoryCacheForKey:key];

如果能取到图片就返回并标记为从内存中取出的

    if (image) {

        doneBlock(image, SDImageCacheTypeMemory);

        return nil;

    }

从硬盘中取创建operation

    NSOperation *operation = [NSOperationnew];

异步函数处理并发对列

    dispatch_async(self.ioQueue, ^{

        if (operation.isCancelled) {

            return;

        }


        @autoreleasepool {

从硬盘中取图片

            UIImage *diskImage = [selfdiskImageForKey:key];

如果取到图片并且需要缓存到内存中

            if (diskImage && self.shouldCacheImagesInMemory) {

                NSUInteger cost = SDCacheCostForImage(diskImage);

                [self.memCachesetObject:diskImageforKey:key cost:cost];

            }

取到图片就返回并标记为从硬盘中取出的

            dispatch_async(dispatch_get_main_queue(), ^{

                doneBlock(diskImage, SDImageCacheTypeDisk);

            });

        }

    });


    return operation;

}

**********************************************************










0 0
原创粉丝点击