SDWebImage源代码阅读(二)

SDWebImage源代码阅读(二)

border="0" width="530" height="100" src="http://music.163.com/outchain/player?type=2&id=29947420&auto=1&height=66">

我们在SDWebImage源代码阅读(一)中讲了下SDWebImage的使用和UIImageView+WebCache

SDWebImageManager:

+ (id)sharedManager {
    static dispatch_once_t once;
    static id instance;
    dispatch_once(&once, ^{
        instance = [self new];
    });
    return instance;
}

SDWebImage是一个单例,OC中的单例就是这么简单

- (instancetype)init {
    SDImageCache *cache = [SDImageCache sharedImageCache];
    SDWebImageDownloader *downloader = [SDWebImageDownloader sharedDownloader];
    return [self initWithCache:cache downloader:downloader];
}

SDwebImageManager每次初始化都会有SDImageSDWebImageDownloader的单例的赋值

- (id <SDWebImageOperation>)downloadImageWithURL:(NSURL *)url
                                         options:(SDWebImageOptions)options
                                        progress:(SDWebImageDownloaderProgressBlock)progressBlock
                                       completed:(SDWebImageCompletionWithFinishedBlock)completedBlock {}

这是SDWebImageManager最关键的方法,实现了,图片的加载保存等功能;progressBlock会在图片下载的时候去调用,completedBlock会在操作完成得时候调用

 if ([url isKindOfClass:NSString.class]) {
        url = [NSURL URLWithString:(NSString *)url];
    }
 if (![url isKindOfClass:NSURL.class]) {
        url = nil;
    }

首先,我们会对url进行一个判断,因为我们将NSString当成NSURL的这种错误的概率是非常高的,所以这里对是NSString类型的字符串进行了转换,保证之后代码的执行。当然,如果url既不是NSURL又不是NSString,这里就会对它进行nil的设置

__block SDWebImageCombinedOperation *operation =[SDWebImageCombinedOperation new];
__weak SDWebImageCombinedOperation *weakOperation = operation;

接下来这里初始化了一个新的SDWebImageCombinedOperation类,并对其进行引用

BOOL isFailedUrl = NO;
@synchronized (self.failedURLs) {
        isFailedUrl = [self.failedURLs containsObject:url];
}

这里@synchronized的作用是创建一个互斥锁,保证没有其他线程对self.failedURLs进行访问修改,起到保护作用。然后查询是否我们正在加载的这个图片的url是曾经加载失败过的url,并对isFailedUrl赋值

if (url.absoluteString.length == 0 || (!(options & SDWebImageRetryFailed) && isFailedUrl)) {
        dispatch_main_sync_safe(^{
            NSError *error = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorFileDoesNotExist userInfo:nil];
            completedBlock(nil, error, SDImageCacheTypeNone, YES, url);
        });
        return operation;
}

当url长度为0,或者是当self.failedURLs中包含了这个url(url代表的图片曾经加载失败),并且options设置不是SDWebImageRetryFailed(失败后重试),dispatch_main_sync_safe主线程同步队列,初始化NSError并且给completedBlock赋值,并且返回SDWebImageOperation变量

 @synchronized (self.runningOperations) {
        [self.runningOperations addObject:operation];
    }

如果url正常,或者错误重试,我们会在self.runningOperations中添加这个操作

NSString *key = [self cacheKeyForURL:url];

根据url返回一个cacheKey,之后可以通过SDWebImageCacheKeyFilterBlock去删除图像网址动态部分

operation.cacheOperation = [self.imageCache queryDiskCacheForKey:key done:^(UIImage *image, SDImageCacheType cacheType) {
}

SDImageCahe的方法通过关键词去查询是否有key的图片存在于高速缓存中或者磁盘中,并且给SDWebImageCombinedOperation的属性cacheOperation赋值。

if (operation.isCancelled) {
    @synchronized (self.runningOperations) {
    [self.runningOperations removeObject:operation];
}
return;

当操作已经被取消operation.isCancelled,我们将操作从self.runningOperations中移除,并且退出该函数

 if ((!image || options & SDWebImageRefreshCached) && (![self.delegate respondsToSelector:@selector(imageManager:shouldDownloadImageForURL:)] || [self.delegate imageManager:self shouldDownloadImageForURL:url])) {
 ···
 }
当图片不存在或者要刷新图片缓存并且状态不属于阻止图片加载
 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);
    });
}

当图片存在,而且需要刷新图片缓存的时候:赋值completedBlock

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

根据options设置downloaderOptions(按位运算符)

id <SDWebImageOperation> subOperation = [self.imageDownloader downloadImageWithURL:url options:downloaderOptions progress:progressBlock completed:^(UIImage *downloadedImage, NSData *data, NSError *error, BOOL finished) {
···
}

通过url和options下载图片

 __strong __typeof(weakOperation) strongOperation = weakOperation;
                if (!strongOperation || strongOperation.isCancelled)

__strong __typeof(weakOperation)因为之前是弱引用,我们需要保证在执行过程中weakOperation不变成nil,所以再加上一个强引用。

当strongOperation不为空或者说操作被取消了

我们什么也不做

当error是nil的时候
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.failedURLs addObject:url];
                        }
                    }

主线程同步队列:如果strongOperation已经变为nil同时操作被取消了,我们给completedBlock赋值。同时error错误原因是我们列出来的,我们将会把url加入到self.failedURLs

当没有error,同时strongOperation为nil且操作没有被取消
 if ((options & SDWebImageRetryFailed)) {
                        @synchronized (self.failedURLs) {
                            [self.failedURLs removeObject:url];
                        }
                    }

当options是失败重试的时候,我们将对self.failedURLs使用互斥锁,并且将url从self.failedURLs移出

 if (options & SDWebImageRefreshCached && image && !downloadedImage) {
                        // Image refresh hit the NSURLCache cache, do not call the completion block
                    }

如果options是刷新图片缓存同时image不是nil同时没有下载的图片,什么都不用执行,(图像刷新NSURLCache缓存,不调用完成闭包)

else if (downloadedImage && (!downloadedImage.images || (options & SDWebImageTransformAnimatedImage)) && [self.delegate respondsToSelector:@selector(imageManager:transformDownloadedImage:withURL:)]) {
                        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];
                                [self.imageCache storeImage:transformedImage recalculateFromImage:imageWasTransformed imageData:(imageWasTransformed ? nil : data) forKey:key toDisk:cacheOnDisk];
                            }
                            dispatch_main_sync_safe(^{
                                if (strongOperation && !strongOperation.isCancelled) {
                                    completedBlock(transformedImage, nil, SDImageCacheTypeNone, finished, url);
           }
      });
  });
}

当已经下载了图片了,且图片是动态图,启用了SDWebImageTransformAnimatedImage,同时,委托有imageManager:transformDownloadedImage:withURL:方法:高优先级异步执行图片转码,当转码完成后图片不为nil,通过self.imageCache保存图片缓存到磁盘上,并且同步执行comletedBlock的赋值

else {
  if (downloadedImage && finished) {
     [self.imageCache storeImage:downloadedImage recalculateFromImage:NO imageData:data forKey:key toDisk:cacheOnDisk];
                        }
                        dispatch_main_sync_safe(^{
                            if (strongOperation && !strongOperation.isCancelled) {
                                completedBlock(downloadedImage, nil, SDImageCacheTypeNone, finished, url);
          }
    });
 }

当图片不是动态图,同时不用刷新图片缓存的时候:如果下载图片完毕,调用self.imageCache将图片缓存保存在磁盘上,同时同步执行comletedBlock的赋值

 if (finished) {
                    @synchronized (self.runningOperations) {
                        if (strongOperation) {
                            [self.runningOperations removeObject:strongOperation];
                        }
                    }
                }

在图片完成保存,或者不需要保存之后,将operation从self.runningOperations移出

operation.cancelBlock = ^{
                [subOperation cancel];
                @synchronized (self.runningOperations) {
                    __strong __typeof(weakOperation) strongOperation = weakOperation;
                    if (strongOperation) {
                        [self.runningOperations removeObject:strongOperation];
                    }
                }
            };

设置操作取消的闭包,在操作取消的时候,我们会将操作从将operation从self.runningOperations移出

当图片存在(同时不刷新图片缓存······就是上面那个条件不成立)
dispatch_main_sync_safe(^{
                __strong __typeof(weakOperation) strongOperation = weakOperation;
                if (strongOperation && !strongOperation.isCancelled) {
                    completedBlock(image, nil, cacheType, YES, url);
                }
            });
            @synchronized (self.runningOperations) {
                [self.runningOperations removeObject:operation];
            }

主线程同步队列:__strong __typeof(weakOperation)因为之前是弱引用,我们需要保证在执行过程中weakOperation不变成nil,所以再加上一个强引用。如果strongOperation已经变为nil或者说操作被取消了,这时,我们需要给completeBlock赋值,最后从self.runningOperations移除这个操作

当图片不存在,同时委托不允许加载图片
dispatch_main_sync_safe(^{
                __strong __typeof(weakOperation) strongOperation = weakOperation;
                if (strongOperation && !weakOperation.isCancelled) {
                    completedBlock(nil, nil, SDImageCacheTypeNone, YES, url);
                }
            });
            @synchronized (self.runningOperations) {
                [self.runningOperations removeObject:operation];

主线程同步队列:__strong __typeof(weakOperation)因为之前是弱引用,我们需要保证在执行过程中weakOperation不变成nil,所以再加上一个强引用。如果strongOperation已经变为nil或者说操作被取消了,这时,我们需要给completeBlock赋值,最后从self.runningOperations移除这个操作,和上面不同的时completeBlock中cacheType是None

简而言之:SDWebImageManager是一个单例管理类,负责协调图片缓存和图片下载,是对 SDImageCache和SDWebImageDownloader的封装,一般我们不用调用SDImageCache、SDWebImageDownloader,直接使用SDWebImageManager即可

@Blog

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值