SDWebImage3.7.5源码阅读一
0. 图片的异步下载
比如在tableview中:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString* cellID = @"cellID";
UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:cellID];
if (cell == nil) {
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellID];
}
[cell.imageView sd_setImageWithURL:[NSURL URLWithString:@"http://www.domain.com/path/to/image.jpg"] placeholderImage:[UIImage imageNamed:@"1.jpg"]];
cell.textLabel.text = @" text ";
return cell;
}
他这里相关的代码就是调用了UIImageView+WebCache
这个分类中的 sd_setImageWithURL:placeholderImage:
方法
1. 开始看代码
- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder {
[self sd_setImageWithURL:url placeholderImage:placeholder options:0 progress:nil completed:nil];
}
发现原先方法调用了另一个参数更多的方法 sd_setImageWithURL:url placeholderImage:nil options:0 progress:nil completed:nil
其中url是图片的url地址,placeholderImag是占位图,options是某个选项?,progress是进度completed肯定就是完成后的操作块
问题1 : 其中options:0 这个options值是具体是什么
1.1 SDWebImageOptions
具体内容
typedef NS_OPTIONS(NSUInteger, SDWebImageOptions) {
//默认情况下 url下载失败,url会被移入黑名单
//这个flag将url从黑名单中移除
//简单来说就是失败后重新下载
SDWebImageRetryFailed = 1 << 0,
//默认图片下载在UI交互的时候开始
//延迟下载
SDWebImageLowPriority = 1 << 1,
//只进行内存缓存
SDWebImageCacheMemoryOnly = 1 << 2,
//默认图片只会在完全下载完显示
//这个flag可以使图片渐进式下载,图片也会逐步显示
SDWebImageProgressiveDownload = 1 << 3,
//刷新缓存
SDWebImageRefreshCached = 1 << 4,
//后台下载
SDWebImageContinueInBackground = 1 << 5,
SDWebImageHandleCookies = 1 << 6,
//允许使用无效的SSL证书
SDWebImageAllowInvalidSSLCertificates = 1 << 7,
//有限加载
SDWebImageHighPriority = 1 << 8,
//延迟占位图
SDWebImageDelayPlaceholder = 1 << 9,
SDWebImageTransformAnimatedImage = 1 << 10,
//图片下载后 手动加载图片
SDWebImageAvoidAutoSetImage = 1 << 11
};
这里options使用0,表示不使用任何选项
2. sd_setImageWithURL
具体实现:
- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletionBlock)completedBlock {
//看名字猜测是取消当前图片加载(任务)
[self sd_cancelCurrentImageLoad];
...
//使用placeholder图片先占位
if (!(options & SDWebImageDelayPlaceholder)) {
dispatch_main_async_safe(^{
self.image = placeholder;
});
}
...
//这里就应该是主要的方法 下载图片了
id <SDWebImageOperation> operation = [SDWebImageManager.sharedManager downloadImageWithURL:url options:options progress:progressBlock completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
...
wself.image = image;
...
}];
//看名字猜测将这个图片下载任务以UIImageViewImageLoad作为关键字储存起来
[self sd_setImageLoadOperation:operation forKey:@"UIImageViewImageLoad"];
...
}
2.1 先看[self sd_cancelCurrentImageLoad]
,
它是调用了以下代码:
- (void)sd_cancelCurrentImageLoad {
[self sd_cancelImageLoadOperationWithKey:@"UIImageViewImageLoad"];
}
sd_cancelImageLoadOperationWithKey
这个方法在 UIView+WebCacheOperation
这个分类中:
- (void)sd_cancelImageLoadOperationWithKey:(NSString *)key {
// Cancel in progress downloader from queue
//会将operation 都储存在operationDictionary?
NSMutableDictionary *operationDictionary = [self operationDictionary];
//取出key对应的operation(s) ,并执行cancel操作,然后将operation(s)从operationDictionary中删除
id operations = [operationDictionary objectForKey:key];
if (operations) {
if ([operations isKindOfClass:[NSArray class]]) {
for (id <SDWebImageOperation> operation in operations) {
if (operation) {
[operation cancel];
}
}
} else if ([operations conformsToProtocol:@protocol(SDWebImageOperation)]){
[(id<SDWebImageOperation>) operations cancel];
}
[operationDictionary removeObjectForKey:key];
}
}
也就是将key为UIImageViewImageLoad的操作都执行cancel操作,最后将key对应的对象全部从operationDictionary中删除
看到这里有几个问题
UIImageViewImageLoad对应的操作是图片正在下载中,还是下载完呢?应该是下载完的操作,因为要是下载中操作都被取消并从operationDictionary中移除,图片也下不成功了。但是要是对应下载完,operation已经被移除,它是依据什么做到同一url不重复下载??url(或者ur对应的特征码)不储存在operation(但是SDWebImageOperation只是一个协议 只声明了cancel操作)中吗?还有operationDictionary的内部储存数据的结构是什么?怎么一个key又可能是数组有可能是单个对象,这样做不是麻烦一点吗?全改成一个key对应一个operation数组不是更方便?所以问题就这几个:
-
问题2:UIImageViewImageLoad对应的是什么操作
-
问题3:operation的实现(SDWebImageOperation中的cancel方法实现 和 内部的属性等)
@protocol SDWebImageOperation <NSObject> - (void)cancel; @end
-
问题4:不重复下载相同url 是根据operation做的,还是根据其他对象实现的
-
问题5:operationDictionary的内部储存数据(能存什么key,key对应的对象是数组还是其他)
- 目前知道有个key 为UIImageViewImageLoad ,看代码猜测当Operation数量为多个时,key对应的是
NSArray<SDWebImageOperation*>
对象,当数量为单个时key对应的是SDWebImageOperation*
对象(或者是考虑到兼容问题?)
- 目前知道有个key 为UIImageViewImageLoad ,看代码猜测当Operation数量为多个时,key对应的是
感觉这几个问题都可以在下载的具体步骤中解决掉。。
2.2 接下来看 dispatch_main_async_safe
它的实现就是一个宏:
#define dispatch_main_async_safe(block)\
if ([NSThread isMainThread]) {\
block();\
} else {\
dispatch_async(dispatch_get_main_queue(), block);\
}
这个我有个蠢问题,为什么要这么做??就算是已经在主线程 但是再执行dispatch_async(dispatch_get_main_queue(), block)也不会错吧??虽然是会感觉多此一举,但是这样做其他的影响呢,会影响性能吗。。
2.3 [self showActivityIndicatorView]
我加上 [cell.imageView setShowActivityIndicatorView:true];也没有看到等待指示器。。是网速太快了还是需要其他设置。。不管了
2.4 [self sd_setImageLoadOperation:operation forKey:@"UIImageViewImageLoad"];
看下它的代码:
- (void)sd_setImageLoadOperation:(id)operation forKey:(NSString *)key {
[self sd_cancelImageLoadOperationWithKey:key];
NSMutableDictionary *operationDictionary = [self operationDictionary];
[operationDictionary setObject:operation forKey:key];
}
它将Operation以key为 UIImageViewImageLoad 存入operationDictionary,而这时Operation对应的下载肯定是异步的,所以UIImageViewImageLoad对应的是图片正在下载中的操作。
- 回答问题2:UIImageViewImageLoad对应的是图片正在下载中的操作。
- 回答问题5(部分):operationDictionary中以 key-operation 方式储存(key-nsarray方式暂时没看到)
若是当第一个图片没下载完,第二图片下载任务进来不就取消之前所有的UIImageViewImageLoad的operation了??那之前的图片怎么下载完?难道cancel之后会自动resume吗?
- 问题6:UIImageViewImageLoad的operation执行 cancel后,在哪里会继续下载?