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
每次初始化都会有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
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