您的位置:首页 > 移动开发 > IOS开发

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

2016-02-24 16:46 931 查看


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会不停地调用直到图片下载完成,此时才会设置finished为YES.

*

* 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:key
done:^(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:url
options: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:downloadedImage
withURL:url];

if (transformedImage && finished) {

BOOL imageWasTransformed = ![transformedImageisEqual:downloadedImage];

[self.imageCachestoreImage:transformedImage
recalculateFromImage: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:downloadedImage
recalculateFromImage:NO
imageData:data forKey:key
toDisk: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];

}

}

};

}
else
if (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:diskImage
forKey:key cost:cost];

}

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

dispatch_async(dispatch_get_main_queue(), ^{

doneBlock(diskImage, SDImageCacheTypeDisk);

});

}

});

return operation;

}

**********************************************************
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: