欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

SDWebImage源码分析

程序员文章站 2024-03-23 21:32:04
...

源码阅读目的

古语说的好“读书破万卷,下笔犹如神”,源码阅读就是一个挖掘别人思路,获取他人精华的一个途径。源码阅读就是站在别人的肩膀上,让自己看的更远。源码阅读对于提高coder的技术水平是非常重要的一个过程。“取其精华,去其糟粕”。所以需要读一些经典的源码,提高自身的实力。世人都说阅读源代码对于功力的提升是十分显著的。

源码主要功能

基本概述

SDWebImage的作用是实现对网络图片的异步下载以及提供图片缓存机制,不影响系统主线程的正常工作。
“This library provides an async image downloader with cache support.”

网络图片下载

SDWebImage开启新线程对图片进行异步下载,不影响系统主线程的正常执行。框架提供了一个下载队列,管理对图片的下载操作。

图片的缓存

SDWebImage对下载过的图片进行了缓存,在一定时间内避免重复下载。直接读取本地或者内存的图片,无需重新下载。

图片的解码

SDWebImage对下载的图片进行重新解码,将二进制数据变成UIImage数据。

源码架构体系

写软件需要有个编写思想:模块化,高内聚,可扩展。我们看到许多优秀的框架都会有这些软件思想。

基本流程

我们这里以UIImageView作为案例,当然SDWebImage也可以用在UIButton,MKAnimationView中,框架对这些控件也增加了相应的分类。
这个框架的设计还是很简洁和优雅,主要的功能只需要一行代码,
[self.imageView sd_setImageWithURL:[NSURL URLWithString:@"url"] placeholderImage:[UIImage imageNamed:@"placeholder.png"]]
其他复杂的实现全部被封装在框架中了,借用别人写的:“麻烦留给自己,方便给别人”。这也是在项目合作中经常遇到的情况,自己负责的模块,对外尽量简洁明了,提供给别人需要的,不需要的无需暴露给调用者。

SDWebImage源码分析

源码框架

SDWebImage 主要包括两方面:图片缓存模块和图片下载模块。该框架采用逐层封装的一个过程。接近调用的一层是高层抽象,与调用者距离最远的是底层实现。如同操作系统的封装是一个模式,底层对细节的具体实现,高层则是最接近使用者的一层,呈现使用者的需要的内容。该框架的一个结构如下图

SDWebImage源码分析

源码细节剖析

在加载图片的方法中为何首先调用 [self sd_cancelCurrentImageLoad]?

- (void)sd_setHighlightedImageWithURL:(NSURL *)url options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletionBlock)completedBlock {
[self sd_cancelCurrentImageLoad];
…..
}
UIImageView在每次加载图片的时候都会产生一个操作Operation,并且将该Opeartion与当前的ImageView进行绑定。因为下载操作都是异步进行的,要确保 在同一时刻只有一个下载操作与当前的UIImageView进行捆绑,因此首先解除该UIImageView与上一个操作的捆绑。

如何避免对同一个URL同一时刻的多次请求?

框架在使用了一个以请求URL为key的字典容器来维护当前的下载,在下载模块SDWebImageDownloader,实现将下载任务的回调放到一个字典容器中,判断当前的key若字典中存在key-value键值对,则表明该URL不是第一次 发起请求,只是将对该请求的blocks回调放在字典容器中,但是不进行新的网络请求,因为已经存在一个对该URL的下载操作了。
BOOL first = NO;
if (!self.URLCallbacks[url]) {
    self.URLCallbacks[url] = [NSMutableArray new];
    first = YES;
}

// Handle single download of simultaneous download request for the same URL
NSMutableArray *callbacksForURL = self.URLCallbacks[url];
NSMutableDictionary *callbacks = [NSMutableDictionary new];
if (progressBlock) callbacks[kProgressCallbackKey] = [progressBlock copy];
if (completedBlock) callbacks[kCompletedCallbackKey] = [completedBlock copy];
[callbacksForURL addObject:callbacks];
self.URLCallbacks[url] = callbacksForURL;

if (first) {
    createCallback();
}

源码涉及技术点

  • NSOperationQueue
  • 线程操作NSOperation
  • GCD
  • 网络请求(URLSession和NSURLTask)
  • 大量的Blocks的使用
  • 可选性(NS_OPTIONS)的使用
  • 系统框架ImageIO的使用
  • 多个回调的使用
  • NSCache
  • 对UIImage支出的图片类型(文件类型:gif,png,jpeg,webp)的底层细节的实现
  • 使用内联函数以及内联函数的作用
  • 常量定义以k开头

需要深入研究的相关技术点

  • ImageIO部分
  • CoreGraphic
  • 图片相关的底层实现

其他学习到内容

代码的书写风格

每一个函数实现单一功能,不要把多个功能的代码放在一个函数中。

SDK的编写格式

最少知道原则(迪米特原则)

暴露给调用者需要用的接口,不需要的无需呈现给用户。

.h中函数的注释说明

/**
 * 函数功能的描述
 *
 * @param 参数名A 对该参数A的说明
 * @param 参数名B 对该参数B的说明
 *
 * @return 对返回值得说明
 */