YYWebImageOperation 源码解析(伪代码,关键流
2019-11-02 本文已影响0人
好有魔力
YYWebImageOperation是支持并发的自定义NSOperation,并且支持线程安全.
重载系统的方法
对于自定义NSOpertion来说,有两个关键父类的方法要重载, 他们是- (void)start 和-(void)cancel,要在这两个方法中处理任务的开始和取消逻辑,并生成相应状态的KVO通知. 剩下就是处理好自定义任务的逻辑,生成相应状态的KVO通知.
- (void)start
- (void)start {
//加锁
[_lock lock];
//检测是否已经被取消
if ([self isCanceld]) {
//在工作线程执行真正的取消逻辑
[ self _cancelOperationOnWorkThread];
//生成KVO通知
self.canceld = YES;
}
//检测是否正在执行
else if ([self isReady] ||
[self executing] ||
![self isFinished]) {
//执行真正的 操作逻辑
[self _startOperation];
//生成KVO通知
self.isExcuting = YES;
}
//
[_lock unlock];
}
- (void)cancel
- (void)cancel {
[_lock lock];
//判断是否被取消过
if (![self isCanceled]) {
//调用父类NSOperation的cancel方法
[super cancel];
//判断是否是正在执行中
if (self.isExecuting) {
self.isExecuting = NO;
在_workThread上执行和真正的取消逻辑
}
//触发取消KVO通知
self.isCanceled = YES;
}
//判断是否开始过
if ([self isStarted]) {
//触发结束的KVO通知
self.isFinished = YES;
}
[_lock unlock];
}
真正的开始逻辑
//伪代码
- (void)_startOperation {
//如果已取消直接return;
if ([self isCanceled]) return;
检测缓存选项 {
从内存缓存中加载图片
//再次判断是否取消
if (!self.isCanceled){
if (如果从内存中加载到图片) {
加锁
调用completion闭包返回给外界
//生成KVO通知
self.isFinished = YES;
self.isExecuting = NO;
解锁
return;
}
}
缓存选项如果不是忽略磁盘缓存 {
在_imageQueue中执行 {
//再次判断是否取消
if (self.isCanceled) return;
让cache从磁盘中取出图片
如果图片存在 {
把图片缓存到内存中
再次判断是否取消,取消则直接返回
加锁
调用completion闭包返回给外界
//生成KVO通知
self.isFinished = YES;
self.isExecuting = NO;
解锁
}
图片不存在 {
再次判断是否取消,取消则直接返回
在子线程下载图片
}
}
}
}
对于支持并发的自定义任务,在进行真正的操作或者返回数据之前,要注意是否被取消(大神的代码处处体现着严谨).
真正的取消逻辑
- (void)_cancelOperation {
if _connection {
if 操作选项是展示菊花 {
隐藏掉菊花
}
[_connection cancel];
_connection = nil;
结束后台任务
//注意这里没有加锁,是因为在调用_cancelOperation之前已经加了锁
调用_completion闭包给外界传递nil图片
}
}
图片下载逻辑
发起请求
- (void)startRequest {
if 取消直接return
在自动释放池中执行 {
if 操作选项是忽略下载失败的URL
并且 这个URL在黑名单(下载失败的url列表)中 {
加锁
调用completion闭包返回给外界 空图片
//生成KVO通知
self.isExecuting = NO;
self.isFinished = YES;
解锁
return;
}
加锁
if 图片URL是FileUrl {
从fileUrl中取出图片的大小,并引用
}
if 没被取消 {
使用NSURLConnection 进行图片下载
<解析操作选项,决定是否要在状态栏上显示菊花>
}
解锁
}
}
下载过程中
//开始接受到响应
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
在autorelease中执行 {
解析response中的状态码,
if 状态码>=400 || 状态码 == 304 {
创建NSError
}
if 有错误 {
调用 _connection 取消方法
调用 _connection 代理收到错误的方法
} else {
拿到 reponse中数据的大小
更新已经数据大小
用数据大小初始化_data
加锁
if 没有取消 {
调用_progress闭包相外界报告图片下载进度 此时是0
}
解锁
}
}
}
//图片数据传输过程中
//代码比较长
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
在autoReleasePool中执行 {
加锁
if 已经取消 {return}
解锁
将获取到的数据放到_data中,
if 没有取消 {
加锁
调用_progress闭包报告进度
解锁
}
获取进度解码器
让进度解码器解码_data
if 已经取消 {return}
if 解析图片的类型是
YYImageTypeUnkwon or
YYWebImageTypeWebP or
YYWebImgeTypeOther {
将YYImageDecoder 置nil;
标记 __progressiveIgnored 为 YES;
return;
}
if 操作选项是 YYWebImageOptionProgressiveBlur {
if 图片类型不是PNG or JPEG {
将YYImageDecoder 置nil;
标记 __progressiveIgnored 为 YES;
return;
}
}
if 解码器解码的帧数是0 {return}
if 操作选项不是 YYWebImageOptionProgressiveBlur {
从YYWebImageCode中取出第0帧 YYImageFrame
if (帧的图片的图片不为nil) {
加锁
if 没有取消 {
调用_completion闭包传递给外界显示
}
解锁
}
return;
} else {
if 图片类型是JPEG {
if <!_progressiveDetected 进度没有检测过> {
从解码器中取出第0帧的属性字典
//这里没有仔细研究ㄟ( ▔, ▔ )ㄏ
取出 kCGImagePropertyJFIFDictionary 属性字典
从 kCGImagePropertyJFIFDictionary 字典中取出 kCGImagePropertyJFIFIsProgressive
if 不支持kCGImagePropertyJFIFIsProgressive {
将YYImageDecoder 置nil;
标记 __progressiveIgnored 为 YES;
return;
}
标记已经检测过 __progressiveDetected = YES
}
从_data中找到 0xFF,0xDA 的范围 markedRange
设置 _progressiveScanedLength = _data.length
if markedRange无效 {return}
if 已经取消 {return}
} else if 图片类型是PNG {
if <!_progressiveDetected 进度没有检测过> {
从解码器中取出第0帧的属性字典
//这里没有仔细研究ㄟ( ▔, ▔ )ㄏ
从第0帧属性字典中取出 kCGImagePropertyPNGDictionary 属性字典
kCGImagePropertyPNGDictionary 属性字典 中取出 kCGImagePropertyPNGInterlaceType
if 不支持kCGImagePropertyPNGInterlaceType {
将YYImageDecoder 置nil;
标记 __progressiveIgnored 为 YES;
return;
}
标记为已经检测 _progressiveDetected = YES;
}
从解码器中取出第0帧 YYImageFrame
if 第0帧中的image为空 {return}
if 已经取消 {return}
计算模糊半径 radius
用计算出来的模糊半径重新生成图片
if 用计算出来的模糊半径重新生成的图片不是空 {
//又是熟悉的操作
加锁
if 没有取消 {
调用_completion闭包发送给外界显示
记录时间戳 _lastProgressiveDecodeTimestamp
}
解锁
}
}
}
}
}
图片下载结束
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
@autoreleasePool {
加锁
置空_connection
if 没有取消 {
_imageQueue 异步执行 {
if 操作选项不是 YYWebImageOptionIgnoreAnimatedImage {
//没有忽略动图
使用_data创建 YYImage 判断是不是动图,
} else {
//忽略动图
直接用YYImageDecoder 解码_data获取第0帧图片
}
解析图片类型:{
如果是WebP,PNG,GIF,JPEG {
if 没有动画 并且格式是GIF,WebP{
清空_data //稍后重新编码到磁盘和内存缓存
}
}
其他情况 {
清空_data
}
}
if 已经取消 return
if transform闭包和前面解码的图片不是空 {
调用 transform 闭包 获取新的图片newImage
if newImage != image {
清空_data
}
引用newImage
if 已经取消 return
在 _newThread 执行 _didReceiveImageFromWeb方法传递image
}
if url是fileURL 并且 操作选项是显示菊花 {
减少菊花数
}
}
}
解锁
}
}
- (void)_didReceiveImageFromWeb:(UIImage *)image {
@autorealsePool {
加锁
if 没有取消 {
if 缓存对象存在 {
if (image || 缓存操作的选项中包含刷新缓存(YYWebImageOptionRefreshImageCache)) {
解析缓存操作的类型,如果设置了忽略磁盘缓存,将缓存类型设置为 内存缓存,否则设置为双缓存
调用_cache的图片缓存方法,传递缓存类型
}
置空_data
if 图片为空 {
创建NSError对象,一会传递出去
if 操作选项是忽略失败的url {
if url 已经包含在 黑名单中 {
更新NSError对象中的错误信息
}else {
将url添加到 黑名单中
}
}
}
if _completion闭包不是空 {
调用_completion闭包,把图片传递出去
}
//生成KVO通知
self.isExecuting = NO;
self.isFinished = YES;
}
}
解锁
}
}
图片下载错误
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
@autoreleasePool {
加锁
if 没有取消 {
if _completion闭包不是空 {
调用_completion闭包 nil image, YYWebImageFromNone,
}
if url是fileURL 并且 操作选项是显示菊花 {
减少菊花数
}
清空_connection
清空_data
//生成KVO通知
self.isExecuting = NO;
self.isFinished = YES;
if (error.code != NSURLErrorNotConnectedToInternet &&
error.code != NSURLErrorCancelled &&
error.code != NSURLErrorTimedOut &&
error.code != NSURLErrorUserCancelledAuthentication &&
error.code != NSURLErrorNetworkConnectionLost) {
将图片url添加到黑名单中
}
}
解锁
}
}
以上就是YYWebImageOperation的开始,取消,和图片下载流程的伪代码,这几个流程看了一遍下来,相信其它部分的代码再看就更容易了.