一、SDWebImage介绍
相信从事iOS开发的各位童鞋对于SDWebImage已经是非常熟悉了,那么接下来我们就进行一些对于SDWebImage的小总结,了解一下SDWebImage内部都做了一些什么操作,完成了什么牛逼的功能,可以让众多下载图片的操作同时进行。
二、SDWebImage操作步骤
在使用SDWebImage的时候,我们使用的方法一般都是- (void)sd_setImageWithURL:(NSURL *)url,或者是- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder,但是无论是哪一个方法,它们内部都是调用的- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletionBlock)completedBlock方法,这个方法就是SDWebImage的核心部分。那我们就看一下这个方法内部都做了一些什么呢
1.[self sd_cancelCurrentImageLoad];首先是调用内部封装好的方法,将之前该ImageView进行的下载操作全部取消,当然这个方法内部还是实现了很多逻辑的,这个我们在后面的实现细节里面再细说,如果你只是来做一个了解的,那么你可以只看主线剧情。
2.objc_setAssociatedObject(self, &imageURLKey, url, OBJC_ASSOCIATION_RETAIN_NONATOMIC);通过runtime的方法,给每一个要进行下载图片操作的imageView通过一个imageURLKey来增加一个url属性。
3.if (!(options & SDWebImageDelayPlaceholder)) {
dispatch_main_async_safe(^{
self.image = placeholder;
});
}。先设置好占位图片
4.接下来也就是最关键的部分了,当url传入的情况下,调用一个已经封装好的方法,idoperation = [SDWebImageManager.sharedManager downloadImageWithURL:url options:options progress:progressBlock completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL),获得一个operation,而获得这个operation的过程,就是SDWebImage的核心部分。
第一步
判断URL是否合法,如果不合法那么就置空。最后也就和错误一起返回了。
if ([url isKindOfClass:NSString.class]) {
url = [NSURL URLWithString:(NSString *)url];
}
if (![url isKindOfClass:NSURL.class]) {
url = nil;
}
第二步
创建一个操作,并且查看,在已经下载失败的url中,是否包含这个url,通过这些标记,来判断下载操作是否可以正常进行下去。然后在加锁的情况下,将操作添加到操作数组中
__block SDWebImageCombinedOperation *operation = [SDWebImageCombinedOperation new];
__weak SDWebImageCombinedOperation *weakOperation = operation;
BOOL isFailedUrl = NO;
@synchronized (self.failedURLs) {
isFailedUrl = [self.failedURLs containsObject:url];
}
if (!url || (!(options & SDWebImageRetryFailed) && isFailedUrl)) {
dispatch_main_sync_safe(^{
NSError *error = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorFileDoesNotExist userInfo:nil];
completedBlock(nil, error, SDImageCacheTypeNone, YES, url);
});
return operation;
}
@synchronized (self.runningOperations) {
[self.runningOperations addObject:operation];
}
第三步(核心部分,也是项目中你有可能要对SDWebImage做修改比较多的部分)
从缓存中获取对应下载操作的key
根据这个key是不是存在,来判断在内存缓存中是不是可以找到对应的图片
NSString *key = [self cacheKeyForURL:url];
operation.cacheOperation = [self.imageCache queryDiskCacheForKey:key done:^(UIImage *image, SDImageCacheType cacheType) {}
- (NSOperation *)queryDiskCacheForKey:(NSString *)key done:(SDWebImageQueryCompletedBlock)doneBlock {
if (!doneBlock) {
return nil;
}
if (!key) {
doneBlock(nil, SDImageCacheTypeNone);
return nil;
}
NSString *newKey = [self getCacheKey:key];(这个方法一般是我们自己来自定义修改key的,比如你公司自己定义的key)
通过这个newlkey从内存缓存中找对应的图片
// First check the in-memory cache...
UIImage *image = [self imageFromMemoryCacheForKey:newKey];
if (image) {
doneBlock(image, SDImageCacheTypeMemory);
return nil;
}
如果找到了,那么久执行block,把图片回传,操作结束
如果找不到,那么就创建新的操作。如果操作被取消了,那么就直接返回
NSOperation *operation = [NSOperation new];
dispatch_async(self.ioQueue, ^{
if (operation.isCancelled) {
return;
}
如果在内存缓存中没有找到,并且下载操作还没有被取消的话,那么就根据newkey从磁盘缓存中查找对应的image。如果找到了,那么就存储到内存缓存中,当下一次再查找相同图片的时候就可以直接从内存缓存中取,提高效率。
@autoreleasepool {
UIImage *diskImage = [self diskImageForKey:newKey];
if (diskImage && self.shouldCacheImagesInMemory) {
NSUInteger cost = SDCacheCostForImage(diskImage);
[self.memCache setObject:diskImage forKey:newKey cost:cost];
}
dispatch_async(dispatch_get_main_queue(), ^{
doneBlock(diskImage, SDImageCacheTypeDisk);
});
}
});
主要的过程基本上就是这样。当然其中还有很多的小细节,比如说清除磁盘缓存啊,怎么样保证异步下载的数据安全啊等等很多,里面也有很多我没有仔细看的方法,在不断的深入过程当中我们再继续研究,看一下其他人的代码是怎么写的,对于我们开阔思路还是很有帮助的。希望大家多多沟通交流。