SDWebImage源码解析

836 阅读2分钟

imageView sd_setImageWithURL 就能直接给imageVIew赋值图片 这个框架的核心类是SDWebImageManager 在外头面 UIImageView+webCache和 UIButton+webCache为下载提供接口。内部有SDWebimageManager 处理协调SDWebImageDownloader和SDWebImageCache SDWebImageDownloader负责处理下载任务 SDWebImageCache负责缓存的工作,添加,删除 查询缓存

UI层主要提供 操作接口

// ==============  UIView+ WebCache.m ============== //

   //valid key:UIImageView || UIButton
   NSString *validOperationKey = operationKey ?: NSStringFromClass([self class]);
   //UIView+WebCacheOperation 的 operationDictionary
   //下面这行代码是保证没有当前正在进行的异步下载操作, 使它不会与即将进行的操作发生冲突
   [self sd_cancelImageLoadOperationWithKey:validOperationKey];
   

   //添加临时的占位图(在不延迟添加占位图的option下)
   if (!(options & SDWebImageDelayPlaceholder)) {
       dispatch_main_async_safe(^{
           [self sd_setImage:placeholder imageData:nil basedOnClassOrViaCustomSetImageBlock:setImageBlock];
       });
   }    
   //如果url存在
   if (url) {     
      ...
       __weak __typeof(self)wself = self;

      //SDWebImageManager下载图片
       id <SDWebImageOperation> operation = [SDWebImageManager.sharedManager loadImageWithURL:url options:options progress:progressBlock completed:^(UIImage *image, NSData *data, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
         
           ...
           //dispatch_main_sync_safe : 保证block能在主线程进行
           dispatch_main_async_safe(^{
               
               if (!sself) {
                   return;
               }               
               if (image && (options & SDWebImageAvoidAutoSetImage) && completedBlock) {
                    //image,而且不自动替换 placeholder image
                   completedBlock(image, error, cacheType, url);
                   return;                    
               } else if (image) {
                   //存在image,需要马上替换 placeholder image
                   [sself sd_setImage:image imageData:data basedOnClassOrViaCustomSetImageBlock:setImageBlock];
                   [sself sd_setNeedsLayout];                
               } else {                    
                   //没有image,在图片下载完之后显示 placeholder image
                   if ((options & SDWebImageDelayPlaceholder)) {
                       [sself sd_setImage:placeholder imageData:nil basedOnClassOrViaCustomSetImageBlock:setImageBlock];
                       [sself sd_setNeedsLayout];
                   }
               }                
               if (completedBlock && finished) {
                   completedBlock(image, error, cacheType, url);
               }
           });
       }];
       
       //在操作缓存字典(operationDictionary)里添加operation,表示当前的操作正在进行
       [self sd_setImageLoadOperation:operation forKey:validOperationKey];        
   } else {
       //如果url不存在,就在completedBlock里传入error(url为空)
       dispatch_main_async_safe(^{
           [self sd_removeActivityIndicator];
           if (completedBlock) {
               NSError *error = [NSError errorWithDomain:SDWebImageErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey : @"Trying to load a nil url"}];
               completedBlock(nil, error, SDImageCacheTypeNone, url);
           }
       });
   }

看一下SDImageCache和 SDWebImageDownder

 // ==============  SDImageCache.m ============== //
- (nullable NSOperation *)queryCacheOperationForKey:(nullable NSString *)key done:(nullable SDCacheQueryCompletedBlock)doneBlock {   
    if (!key) {
        if (doneBlock) {
            doneBlock(nil, nil, SDImageCacheTypeNone);
        }
        return nil;
    }		
    //================查看内存的缓存=================//
    UIImage *image = [self imageFromMemoryCacheForKey:key];    
    // 如果存在,直接调用block,将image,data,CaheType传进去
    if (image) {    
        NSData *diskData = nil;        
        //如果是gif,就拿到data,后面要传到doneBlock里。不是gif就传nil
        if ([image isGIF]) {
            diskData = [self diskImageDataBySearchingAllPathsForKey:key];
        }        
        if (doneBlock) {
            doneBlock(image, diskData, SDImageCacheTypeMemory);
        }        
        //因为图片有缓存可供使用,所以不用实例化NSOperation,直接范围nil
        return nil;
    }
    //================查看磁盘的缓存=================//
    NSOperation *operation = [NSOperation new];    
    //唯一的子线程:self.ioQueue
    dispatch_async(self.ioQueue, ^{        
        if (operation.isCancelled) {
            // 在用之前就判断operation是否被取消了,作者考虑的非常严谨
            return;
        }
        @autoreleasepool {            
            NSData *diskData = [self diskImageDataBySearchingAllPathsForKey:key];
            UIImage *diskImage = [self diskImageForKey:key];            
            if (diskImage && self.config.shouldCacheImagesInMemory) {
                  // cost 被用来计算缓存中所有对象的代价。当内存受限或者所有缓存对象的总代价超过了最大允许的值时,缓存会移除其中的一些对象。
                NSUInteger cost = SDCacheCostForImage(diskImage);                
                //存入内存缓存中
                [self.memCache setObject:diskImage forKey:key cost:cost];
            }
            if (doneBlock) {
                dispatch_async(dispatch_get_main_queue(), ^{
                    doneBlock(diskImage, diskData, SDImageCacheTypeDisk);
                });
            }
        }
    });

    return operation;
}

参考资料 : knightsj.github.io/2017/01/24/…