【iOS】—— SDWebImage源码学习(1)(未完)

iOS】------ SDWebImage源码学习

SDWebImage

SDWebImage具有缓存支持的异步映像下载程序。并添加了像UI元素分类类UIImageView、UIButton、MKAnnotationView,可以直接为这些UI元素添加图片。

基础使用

在日常的使用中,通常是加载网络图片到UIImageView上展示,所以一般在需要使用SDWebImage的文件中只引用#import "UIImageView+WebCache.h"头文件。

最简单的加载方式是只加载图片地址:

objectivec 复制代码
UIImageView *imageView = [[UIImageView alloc] initWithFrame:self.view.bounds];
[self.view addSubview:imageView];
    
[imageView sd_setImageWithURL:[NSURL URLWithString:@"http://www.domain.com/path/to/image.jpg"]];

SDWebImage也提供了其他的加载方法,不过点击方法进入查看后,发现最终都是调用其全能方法:

objectivec 复制代码
- (void)sd_setImageWithURL:(nullable NSURL *)url {
    [self sd_setImageWithURL:url placeholderImage:nil options:0 progress:nil completed:nil];
}

全能方法除了必需的的图片地址,还提供了占位图、可选项、加载进度和完成回调。

objectivec 复制代码
- (void)sd_setImageWithURL:(nullable NSURL *)url
          placeholderImage:(nullable UIImage *)placeholder
                   options:(SDWebImageOptions)options
                  progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
                 completed:(nullable SDExternalCompletionBlock)completedBlock;

占位图像: 在网络图片加载过程中,ImageView会显示占位图像,给用户一个视觉上的暂时反馈。当网络图片加载完成后,ImageView中会显示加载完成的图片。
选项: 是一个枚举类型的值,用于设置加载图片的选项。其中包含了一些常用的选项,例如缓存策略、图片解码方式、加载优先级等。通过传递不同的选项,可以对图片加载的行为进行定制。
进度: 它们提供了对图片加载过程中的进度和结果的反馈。它们提供了对图片加载过程中的进度和结果的反馈。
完成回调: 是一个块对象,用于在图片加载完成后执行相应的操作。

点击进入全能方法中:

objectivec 复制代码
- (void)sd_setImageWithURL:(nullable NSURL *)url
          placeholderImage:(nullable UIImage *)placeholder
                   options:(SDWebImageOptions)options
                  progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
                 completed:(nullable SDExternalCompletionBlock)completedBlock {
    [self sd_internalSetImageWithURL:url
                    placeholderImage:placeholder
                             options:options
                        operationKey:nil
                       setImageBlock:nil
                            progress:progressBlock
                           completed:completedBlock];
}

全能方法并没有什么实际的实现,只是对另一个方法的封装。

主要功能

  1. 对UIImageView、UIButton、MKAnnotationView添加Web图像和告诉缓存管理。
  2. 异步图像下载器。
  3. 具有自动缓存到期处理的异步内存+磁盘映像缓存。
  4. 背景图像解压缩。
  5. 对动画图像的支持。
  6. 可以自定义和组合的转换,可在下载后立即应用于图像。
  7. 可以自定义加载器(如照片库)来扩展图像加载功能。
  8. 加载中的indicator显示。
  9. 保证不会下载相同的URL。
  10. 下载过程或者资源保存过程用到了GCD和ARC。
  11. 提前将获取到的图片放到主线程,保证不会阻塞主线程。

获取图片缓存

在图片加载的方法实现中,可以看到有比较重要的两个方法:一个是获取图片缓存,另一个是从网络下载图片。 在这一节,我们先看获取图片缓存:

objectivec 复制代码
- (nullable NSOperation *)queryCacheOperationForKey:(nullable NSString *)key options:(SDImageCacheOptions)options done:(nullable SDCacheQueryCompletedBlock)doneBlock;

点击方法进入查看其实现:

objectivec 复制代码
- (nullable NSOperation *)queryCacheOperationForKey:(nullable NSString *)key options:(SDImageCacheOptions)options done:(nullable SDCacheQueryCompletedBlock)doneBlock {
    // 如果没传参数key就直接回调并返回,就不继续向下执行了
    if (!key) {
        if (doneBlock) {
            doneBlock(nil, nil, SDImageCacheTypeNone);
        }
        return nil;
    }
    
    // 先根据key查找内存中是否有缓存
    UIImage *image = [self imageFromMemoryCacheForKey:key];
    // 如果有缓存图片,并且没设置强制从硬盘中查找缓存,就直接回调并返回了
    BOOL shouldQueryMemoryOnly = (image && !(options & SDImageCacheQueryDataWhenInMemory));
    if (shouldQueryMemoryOnly) {
        if (doneBlock) {
            doneBlock(image, nil, SDImageCacheTypeMemory);
        }
        return nil;
    }
    
    // 生成一个操作对象
    NSOperation *operation = [NSOperation new];
    // 生成一个查询硬盘缓存的代码块
    void(^queryDiskBlock)(void) =  ^{
        // 如果操作取消就直接返回,不执行回调
        if (operation.isCancelled) {
            // do not call the completion if cancelled
            return;
        }
        
        // 生成一个自动释放池
        @autoreleasepool {
            // 根据key查找硬盘中是否有缓存
            NSData *diskData = [self diskImageDataBySearchingAllPathsForKey:key];
            UIImage *diskImage;
            SDImageCacheType cacheType = SDImageCacheTypeDisk;
            if (image) {
                // 如果内存中有缓存
                diskImage = image;
                cacheType = SDImageCacheTypeMemory;
            } else if (diskData) {
                // 如果内存中没有缓存但是硬盘中有缓存
                diskImage = [self diskImageForKey:key data:diskData];
                if (diskImage && self.config.shouldCacheImagesInMemory) {
                    NSUInteger cost = SDCacheCostForImage(diskImage);
                    [self.memCache setObject:diskImage forKey:key cost:cost];
                }
            }
            
            // 如果设置了同步查询硬盘缓存的选项就直接调用,否则就主队列异步回调
            if (doneBlock) {
                if (options & SDImageCacheQueryDiskSync) {
                    doneBlock(diskImage, diskData, cacheType);
                } else {
                    dispatch_async(dispatch_get_main_queue(), ^{
                        doneBlock(diskImage, diskData, cacheType);
                    });
                }
            }
        }
    };
    
    // 如果设置了同步查询硬盘缓存的选项就直接调用,否则就自定义串行队列异步回调
    if (options & SDImageCacheQueryDiskSync) {
        queryDiskBlock();
    } else {
        dispatch_async(self.ioQueue, queryDiskBlock);
    }
    
    return operation;
}

获取图片缓存的逻辑还是很清晰的:

  • 首先查找在内存中的缓存,再根据设置的选项决定要不要继续查找。
  • 然后根据设置的选项决定是同步还是异步查找硬盘中的缓存。
  • 接着根据设置的选项决定要不要把硬盘中的缓存图片缓存到内存中。
  • 最后进行回调数据。

缓存机制

独立的异步图像下载

可能会用到单独的异步图片下载,则一定要用- (id <SDWebImageOperation>)downloadImageWithURL:(NSURL *)url options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletionWithFinishedBlock)completedBlock;来建立一个SDWebImageDownLoader 的实例。这样就可以有下载进度的回调和下载完成的回调,可以在回调完成进度条相关的操作和显示图片相关的操作。

独立的异步图像缓存

SDImageCache类提供一个管理缓存的单例类。

objectivec 复制代码
SDImageCache *imageCache = [SDImageCache sharedImageCache];

查找和缓存图片时以URL作为key。(先查找内存,如果内存不存在该图片,再查找硬盘;查找硬盘时,以URL的MD5值作为key)。

查找机制如下:

  • Memory(内存)中查找:SDImageCache 类的 queryDiskCacheForKey 方法用于查询图片缓存。queryDiskCacheForKey 方法先会查询 Memory Cache ,如果查找到就直接返回,反之进入下面的硬盘查找。
  • Disk(磁盘) 中查找:如果 Memory Cache 查找不到, 就会查询 Disk Cache。就是如果 Disk Cache 查询成功,会把得到的图片再次设置到 Memory Cache 中, 以便最大化那些高频率展现图片的效率。如果找不到就进入下面的网络下载。

缓存机制如下:

  • 内存缓存的处理是使用 NSCache 对象来实现的。NSCache 是一个类似于集合的容器。它存储 key-value 对,这一点类似于 NSDictionary 类。 我们通常用使用缓存来临时存储短时间使用但创建昂贵的对象。重用这些对象可以优化性能,因为它们的值不需要重新计算。另外一方面,这些对象对于程序来说不是紧要的,在内存紧张时会被丢弃,所以有Disk(硬盘)缓存清理策略。
  • SDWebImage 使用 NSFileManager 对象来实现磁盘缓存,图片存储的位置位于Cache文件夹。另外,SDImageCache 还定义了一个串行队列,来异步存储图片。 SDImageCache 提供了大量方法来缓存、获取、移除及清空图片。而对于每个图片,为了方便地在内存或磁盘中对它进行这些操作,我们需要一个 key 值来索引它。 在内存中,我们将其作为 NSCache 的 key 值,而在磁盘中,我们用这个 key 作为图片的文件名。 对于一个远程服务器下载的图片,其 url 理所当然作为这个 key 值。


核心类:

  • SDWebImageDownloader: 负责维持图片的下载队列,是一个单例对象
  • SDWebImageDownloaderOperation: 负责真正的图片下载请求,一个自定义的并行Operation子类
  • SDImageCache: 负责SDWebImage的缓存工作,是一个单例对象
  • SDWebImageManager: 是总的管理类,维护了一个SDWebImageDownloader实例和一个SDImageCache实例,是下载与缓存的桥梁
  • SDWebImageDecoder: 负责图片的解压缩
  • SDWebImagePrefetcher: 负责图片的预取
  • UIImageView+WebCache: 和其他的扩展都是与用户直接打交道的。

概念框架

  • UIImageView+WebCache和UIButton+WebCache直接为表层的UIKit框架提供接口。
  • SDWebImageManger负责处理和协调SDWebImageDownloader和SDWebImageCache,并与 UIKit层进行交互。
  • SDWebImageDownloaderOperation真正执行下载请求,最底层的两个类为高层抽象提供支持。

这段时间主要先梳理源码的框架,以及源码的基础内容,没有时间对源码进行深入解读。

相关推荐
叽哥2 小时前
Flutter Riverpod上手指南
android·flutter·ios
用户091 天前
SwiftUI Charts 函数绘图完全指南
ios·swiftui·swift
YungFan1 天前
iOS26适配指南之UIColor
ios·swift
权咚2 天前
阿权的开发经验小集
git·ios·xcode
用户092 天前
TipKit与CloudKit同步完全指南
ios·swift
法的空间2 天前
Flutter JsonToDart 支持 JsonSchema
android·flutter·ios
2501_915918412 天前
iOS 上架全流程指南 iOS 应用发布步骤、App Store 上架流程、uni-app 打包上传 ipa 与审核实战经验分享
android·ios·小程序·uni-app·cocoa·iphone·webview
2303_Alpha2 天前
SpringBoot
笔记·学习
萘柰奈2 天前
Unity学习----【进阶】TextMeshPro学习(三)--进阶知识点(TMP基础设置,材质球相关,两个辅助工具类)
学习·unity
沐矢羽2 天前
Tomcat PUT方法任意写文件漏洞学习
学习·tomcat