您的位置:首页 > 移动开发 > IOS开发

SDWebImage源码分析

2017-08-22 14:50 211 查看

源码阅读目的

古语说的好“读书破万卷,下笔犹如神”,源码阅读就是一个挖掘别人思路,获取他人精华的一个途径。源码阅读就是站在别人的肩膀上,让自己看的更远。源码阅读对于提高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 主要包括两方面:图片缓存模块和图片下载模块。该框架采用逐层封装的一个过程。接近调用的一层是高层抽象,与调用者距离最远的是底层实现。如同操作系统的封装是一个模式,底层对细节的具体实现,高层则是最接近使用者的一层,呈现使用者的需要的内容。该框架的一个结构如下图




源码细节剖析

在加载图片的方法中为何首先调用 [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 对返回值得说明
*/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息