美文网首页
SDWebImage源码分析

SDWebImage源码分析

作者: Jey | 来源:发表于2020-12-28 10:23 被阅读0次

4个模块

  • SDWebImageManager管理器
  • 缓存机制
  • 下载管理
  • UIKit模块
截屏2020-12-28 下午2.46.50.png

工作流程


截屏2020-12-28 上午10.50.44.png
关键类

SDWebImageManager的核心,负责发起下载和缓存。
SDImageCeche,缓存对象,负责内存和沙盒的缓存。
SDWebImageDownloader负责所有图片的下载操作。
SDWebImageDownloaderOperation负责单个文件的下载。

SDWebImageManager图片加载的流程
  • downloadImageWithURL:options:progress:completed: 中会先拿图片缓存的 key 去 SDImageCache 中读取内存缓存,如果有,就返回给 SDWebImageManager;如果内存缓存没有,就开启异步线程,拿经过 MD5 处理的key 去读取磁盘缓存,如果找到磁盘缓存了,就同步到内存缓存中去,然后再返回给 SDWebImageManager。

  • 如果内存缓存和磁盘缓存中都没有,SDWebImageManager 就会调用 SDWebImageDownloader的方法去下载

-downloadImageWithURL: options: progress: completed:

该会先将传入的 progressBlock 和 completedBlock 保存起来,并在第一次下载该 URL 的图片时,创建一个 NSMutableURLRequest 对象和一个 SDWebImageDownloaderOperation 对象,并将该 SDWebImageDownloaderOperation 对象添加到 SDWebImageDownloader 的downloadQueue 来启动异步下载任务,使用了NSOperationQueue这种多线程,更加对象化,可取消图片的下载操作。

  • SDWebImageDownloaderOperation 中包装了一个 NSURLSession的网络请求,在URLSession的回调中拿到图片数据,开始异步解码操作,并最终回调 completedBlock。

  • SDWebImageDownloaderOperation 中的图片下载请求完成后,会回调给 SDWebImageDownloader,然后 SDWebImageDownloader 再回调给 SDWebImageManager,SDWebImageManager 中再将图片分别缓存到内存和磁盘上,GCD异步到回到主线程中设置UIImageView的 image 属性。

SDImageCeche清理缓存
  1. 初始化NSCache的时候,注册了通知,收到了内存警告就清理,最大存储时间是7天,到了也清理。
  2. 分为clearMemory,clearDisk.
  3. 清理磁盘流程,先找到磁盘目录,遍历创建文件目录的枚举器,找到过期的文件,添加到一个数组中,没有过期的文件,以键值形式存到一个字典中。
  4. 遍历删除过期的文件。
  5. 再检测磁盘的缓存大小,超过最大缓存值,把上一步缓存的文件根据修改时间排序,遍历删除,直到最大缓存大小的一半(时间最早的最先删除)。
SDWebImageDownloader下载

最大并发数,在SDWebImageDownloaderConfig中默认了

        _maxConcurrentDownloads = 6;
        _downloadTimeout = 15.0;

关键代码分析

  1. UIKit调用sd的时候都会走到UIView+WebCache中的,为什么都走这里呢,方便统一管理取消任务
- (void)sd_internalSetImageWithURL:(nullable NSURL *)url
                  placeholderImage:(nullable UIImage *)placeholder
                           options:(SDWebImageOptions)options
                           context:(nullable SDWebImageContext *)context
                     setImageBlock:(nullable SDSetImageBlock)setImageBlock
                          progress:(nullable SDImageLoaderProgressBlock)progressBlock
                         completed:(nullable SDInternalCompletionBlock)completedBlock;

第一步:判断这个View上有没有在执行任务,经常出现在UITableview中

if (!validOperationKey) {
        // pass through the operation key to downstream, which can used for tracing operation or image view class
        validOperationKey = NSStringFromClass([self class]);
        SDWebImageMutableContext *mutableContext = [context mutableCopy];
        mutableContext[SDWebImageContextSetImageOperationKey] = validOperationKey;
        context = [mutableContext copy];
    }
    self.sd_latestOperationKey = validOperationKey;
    [self sd_cancelImageLoadOperationWithKey:validOperationKey];

如果有,取消当前任务,通过当前view的key,这个可以值存在UIView+WebCacheOperation中

- (void)sd_cancelImageLoadOperationWithKey:(nullable NSString *)key {
    if (key) {
        // Cancel in progress downloader from queue
        SDOperationsDictionary *operationDictionary = [self sd_operationDictionary];
        id<SDWebImageOperation> operation;
        
        @synchronized (self) {
            operation = [operationDictionary objectForKey:key];
        }
        if (operation) {
            if ([operation conformsToProtocol:@protocol(SDWebImageOperation)]) {
                [operation cancel];
            }
            @synchronized (self) {
                [operationDictionary removeObjectForKey:key];
            }
        }
    }
}

- (SDOperationsDictionary *)sd_operationDictionary {
    @synchronized(self) {
        SDOperationsDictionary *operations = objc_getAssociatedObject(self, &loadOperationKey);
        if (operations) {
            return operations;
        }
        operations = [[NSMapTable alloc] initWithKeyOptions:NSPointerFunctionsStrongMemory valueOptions:NSPointerFunctionsWeakMemory capacity:0];
        objc_setAssociatedObject(self, &loadOperationKey, operations, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
        return operations;
    }
}

添加operation,在

- (void)sd_setImageLoadOperation:(nullable id<SDWebImageOperation>)operation forKey:(nullable NSString *)key {
    if (key) {
        [self sd_cancelImageLoadOperationWithKey:key];
        if (operation) {
            SDOperationsDictionary *operationDictionary = [self sd_operationDictionary];
            @synchronized (self) {
                [operationDictionary setObject:operation forKey:key];
            }
        }
    }
}

这个过程保证了一个UIImageView只会添加一次任务。
id <SDWebImageOperation> operation类型,它是一个

  1. 接下来就是初始化SDWebImageManager,还有一些进度的block

  2. 进入管理类的loadImageWithURL方法,有缓存就加载缓存,没缓存就网络获取。在completed回调中可以看出SDImageCacheType

id <SDWebImageOperation> operation = [manager loadImageWithURL:url options:options context:context progress:combinedProgressBlock completed:^(UIImage *image, NSData *data, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {

这一步主要看SDWebImageCache类,怎么获取缓存图片,存储图片,管理磁盘内存。清理缓存看上面介绍的SDImageCeche清理缓存

  1. 内存缓存的具体操作,内存缓存中SDMemoryCache有一段存取的代码
- (void)setObject:(id)obj forKey:(id)key cost:(NSUInteger)g {
    [super setObject:obj forKey:key cost:g];
    if (!self.config.shouldUseWeakMemoryCache) {
        return;
    }
    if (key && obj) {
        // Store weak cache
        SD_LOCK(_weakCacheLock);
        [self.weakCache setObject:obj forKey:key];
        SD_UNLOCK(_weakCacheLock);
    }
}

- (id)objectForKey:(id)key {
    id obj = [super objectForKey:key];
    if (!self.config.shouldUseWeakMemoryCache) {
        return obj;
    }
    if (key && !obj) {
        // Check weak cache
        SD_LOCK(_weakCacheLock);
        obj = [self.weakCache objectForKey:key];
        SD_UNLOCK(_weakCacheLock);
        if (obj) {
            // Sync cache
            NSUInteger cost = 0;
            if ([obj isKindOfClass:[UIImage class]]) {
                cost = [(UIImage *)obj sd_memoryCost];
            }
            [super setObject:obj forKey:key cost:cost];
        }
    }
    return obj;
}

SDMemoryCache重写了NSCache的存取方法
问题:

  1. SDMemoryCache继承系统的NSCache
  2. 但是发现缓存中使用的self.weakCache是NSMapTable,而且存储的时候调用了父类的set方法 [super setObject:obj forKey:key cost:g];
  3. 这样内存缓存中就有两份缓存了,这是为什么?
  4. 系统缓存可能随时被清理,所以存储的时候存储两份,一份系统,一份自己维护
  5. 先去系统NSCache中去,没有就在self.weakCache中找,找到了再次[super setObject:obj forKey:key cost:cost];
  6. 提高了查找速度
  1. SDImageCeche缓存中使用GCD的异步串行解码、计算尺寸大小、删除文件操作
dispatch_async(self.ioQueue, ^{
        @autoreleasepool {
            NSData *data = imageData;
            if (!data && [image conformsToProtocol:@protocol(SDAnimatedImage)]) {
                // If image is custom animated image class, prefer its original animated data
                data = [((id<SDAnimatedImage>)image) animatedImageData];
            }
            if (!data && image) {
                // Check image's associated image format, may return .undefined
                SDImageFormat format = image.sd_imageFormat;
                if (format == SDImageFormatUndefined) {
                    // If image is animated, use GIF (APNG may be better, but has bugs before macOS 10.14)
                    if (image.sd_isAnimated) {
                        format = SDImageFormatGIF;
                    } else {
                        // If we do not have any data to detect image format, check whether it contains alpha channel to use PNG or JPEG format
                        format = [SDImageCoderHelper CGImageContainsAlpha:image.CGImage] ? SDImageFormatPNG : SDImageFormatJPEG;
                    }
                }
                data = [[SDImageCodersManager sharedManager] encodedDataWithImage:image format:format options:nil];
            }
            [self _storeImageDataToDisk:data forKey:key];
            [self _archivedDataWithImage:image forKey:key];
        }
        
        if (completionBlock) {
            dispatch_async(dispatch_get_main_queue(), ^{
                completionBlock();
            });
        }
    });

相关文章

  • SDWebImage 源码分析

    SDWebImage 源码分析 首先我 fork 了 SDWebImage 的源码,见 conintet/SDWe...

  • SDWebImage源码分析

    SDWebImage源码分析 UIImageView+WebCache sd SDWebImageManager ...

  • SDWebImage源码分析

    技术无极限,从菜鸟开始,从源码开始。 由于公司目前项目还是用OC写的项目,没有升级swift 所以暂时SDWebI...

  • SDWebImage源码分析

    SDWebImage介绍 UIView+WebCache类别: 所有设置图片的方法内部都会且仅调用此方法 UIVi...

  • SDWebImage源码分析

    SDWebImage是专门用于图片下载的框架,内部封装了图片异步下载和缓存的过程,涉及到的知识点有:(1)runt...

  • SDWebImage源码分析

    SDWebImage库是一个支持缓存的异步图片下载库。为了方便使用,它提供了UIImageView, UIButt...

  • SDWebImage源码分析

    一、SDWebImage到底是什么 Asynchronous image downloader with cach...

  • SDWebImage源码分析

    使用SDWebImage无非就是做图片缓存 一起来看下SDWebImage的几种使用方式:SDWebImage下载...

  • 源码分析SDWebImage

    SDWebImage是一个开源的第三方库,它提供了UIImageView的一个分类,以支持从远程服务器下载并缓存图...

  • 源码分析SDWebImage

    /* *ThisfileispartoftheSDWebImagepackage. *(c)OlivierPoit...

网友评论

      本文标题:SDWebImage源码分析

      本文链接:https://www.haomeiwen.com/subject/wxeznktx.html