美文网首页
YYWebImageOperation 源码解析(伪代码,关键流

YYWebImageOperation 源码解析(伪代码,关键流

作者: 好有魔力 | 来源:发表于2019-11-02 15:48 被阅读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的开始,取消,和图片下载流程的伪代码,这几个流程看了一遍下来,相信其它部分的代码再看就更容易了.

    相关文章

      网友评论

          本文标题:YYWebImageOperation 源码解析(伪代码,关键流

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