SDWebImage实现原理

2018-05-11  本文已影响18人  ChancePro
1、类图
SDWebImageClassDiagram.png
2、时序图
SDWebImageSequenceDiagram.png
3、目录结构
Downloader
Cache
Utils
Categories
Other
4、流程
[imageView sd_setImageWithURL:url placeholderImage:placeholderImage]
- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletionBlock)completedBlock {
    [self sd_cancelCurrentImageLoad];
    objc_setAssociatedObject(self, &imageURLKey, url, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

    if (!(options & SDWebImageDelayPlaceholder)) {
        dispatch_main_async_safe(^{
            self.image = placeholder;
        });
    }
    
    if (url) {

        // check if activityView is enabled or not
        if ([self showActivityIndicatorView]) {
            [self addActivityIndicator];
        }

        __weak __typeof(self)wself = self;
        id <SDWebImageOperation> operation = [SDWebImageManager.sharedManager downloadImageWithURL:url options:options progress:progressBlock completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
            [wself removeActivityIndicator];
            if (!wself) return;
            dispatch_main_sync_safe(^{
                if (!wself) return;
                if (image && (options & SDWebImageAvoidAutoSetImage) && completedBlock)
                {
                    completedBlock(image, error, cacheType, url);
                    return;
                }
                else if (image) {
                    wself.image = image;
                    [wself setNeedsLayout];
                } else {
                    if ((options & SDWebImageDelayPlaceholder)) {
                        wself.image = placeholder;
                        [wself setNeedsLayout];
                    }
                }
                if (completedBlock && finished) {
                    completedBlock(image, error, cacheType, url);
                }
            });
        }];
        [self sd_setImageLoadOperation:operation forKey:@"UIImageViewImageLoad"];
    } else {
        dispatch_main_async_safe(^{
            [self removeActivityIndicator];
            if (completedBlock) {
                NSError *error = [NSError errorWithDomain:SDWebImageErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey : @"Trying to load a nil url"}];
                completedBlock(nil, error, SDImageCacheTypeNone, url);
            }
        });
    }
}

经过层层调用,直到 UIImageView+WebCache 中最核心的方法 sd_setImageWithURL: placeholderImage: options: progress: completed: 该方法中,主要做了以下几件事:

  1. [self sd_cancelCurrentImageLoad];取消当前正在进行的加载任务operation
  2. self.image = placeholder;设置placeholder
  3. 如果url不为nil,就通过SDWebImageManager单例开启图片加载任务operation,SDWebImageManager的图片加载方法中会返回一个SDWebImageCombineOperation对象,这个对象包含一个cacheOperation和一个cancelBlock。

SDWebImageManager 的图片加载方法 downloadImageWithURL:options:progress:completed: 中

  1. 会先拿图片缓存的 key (这个 key 默认是图片 URL)去 SDImageCache 中读取内存缓存,如果有,就返回给 SDWebImageManager;
  2. 如果内存缓存没有,就开启异步线程,拿经过 MD5 处理的 key 去读取磁盘缓存,如果找到磁盘缓存了,就同步到内存缓存中去,然后再返回给 SDWebImageManager。
  3. 如果内存缓存和磁盘缓存中都没有,SDWebImageManager 就会调用 SDWebImageDownloader 单例的 -downloadImageWithURL: options: progress: completed: 方法去下载,该会先将传入的 progressBlock 和 completedBlock 保存起来,并在第一次下载该 URL 的图片时,创建一个 NSMutableURLRequest 对象和一个 SDWebImageDownloaderOperation 对象,并将该 SDWebImageDownloaderOperation 对象添加到 SDWebImageDownloader 的downloadQueue 来启动异步下载任务。

SDWebImageDownloaderOperation 中包装了一个 NSURLConnection 的网络请求,并通过 runloop 来保持 NSURLConnection 在 start 后、收到响应前不被干掉,下载图片时,监听 NSURLConnection 回调的 -connection:didReceiveData: 方法中会负责 progress 相关的处理和回调,- connectionDidFinishLoading: 方法中会负责将 data 转为 image,以及图片解码操作,并最终回调 completedBlock。
SDWebImageDownloaderOperation 中的图片下载请求完成后,会回调给 SDWebImageDownloader,然后 SDWebImageDownloader 再回调给 SDWebImageManager,SDWebImageManager 中再将图片分别缓存到内存和磁盘上(可选),并回调给 UIImageView,UIImageView 中再回到主线程设置 image 属性。至此,图片的下载和缓存操作就结束了。

SDWebImage使用LRU缓存淘汰算法。缓存有效期默认是7天。

上一篇 下一篇

猜你喜欢

热点阅读