您的位置:首页 > 编程语言

SDWebImage源代码阅读(二)

2016-09-13 23:06 11 查看

SDWebImage源代码阅读(二)

我们在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
每次初始化都会有
SDImage
SDWebImageDownloader
的单例的赋值

- (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

SDWebImageDownloaderOpti
4000
ons 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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: