• SDWebImage3.7.5源码阅读一



    0. 图片的异步下载

    比如在tableview中:

    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
    
    static NSString* cellID  = @"cellID";
    
    UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:cellID];
    if (cell == nil) {
        cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellID];
    }
    
    [cell.imageView sd_setImageWithURL:[NSURL URLWithString:@"http://www.domain.com/path/to/image.jpg"] placeholderImage:[UIImage imageNamed:@"1.jpg"]];
    cell.textLabel.text = @" text ";
    
    return cell;
    
    }
    

    他这里相关的代码就是调用了UIImageView+WebCache这个分类中的 sd_setImageWithURL:placeholderImage:方法


    1. 开始看代码


    - (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder {
    [self sd_setImageWithURL:url placeholderImage:placeholder options:0 progress:nil completed:nil];
    }
    

    发现原先方法调用了另一个参数更多的方法 sd_setImageWithURL:url placeholderImage:nil options:0 progress:nil completed:nil

    其中url是图片的url地址,placeholderImag是占位图,options是某个选项?,progress是进度completed肯定就是完成后的操作块

    问题1 : 其中options:0 这个options值是具体是什么

    1.1 SDWebImageOptions具体内容

    typedef NS_OPTIONS(NSUInteger, SDWebImageOptions) {
    //默认情况下 url下载失败,url会被移入黑名单
    //这个flag将url从黑名单中移除
    //简单来说就是失败后重新下载
    SDWebImageRetryFailed = 1 << 0,
    
     //默认图片下载在UI交互的时候开始
     //延迟下载
    SDWebImageLowPriority = 1 << 1,
    
    //只进行内存缓存
    SDWebImageCacheMemoryOnly = 1 << 2,
    
     //默认图片只会在完全下载完显示
     //这个flag可以使图片渐进式下载,图片也会逐步显示
    SDWebImageProgressiveDownload = 1 << 3,
    
    //刷新缓存
    SDWebImageRefreshCached = 1 << 4,
    
    //后台下载
    SDWebImageContinueInBackground = 1 << 5,
    
    SDWebImageHandleCookies = 1 << 6,
    
    //允许使用无效的SSL证书
    SDWebImageAllowInvalidSSLCertificates = 1 << 7,
    
    //有限加载
    SDWebImageHighPriority = 1 << 8,
    
    //延迟占位图
    SDWebImageDelayPlaceholder = 1 << 9,
    
    SDWebImageTransformAnimatedImage = 1 << 10,
    
    //图片下载后 手动加载图片
    SDWebImageAvoidAutoSetImage = 1 << 11
    };
    

    这里options使用0,表示不使用任何选项

    2. sd_setImageWithURL具体实现:

    - (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletionBlock)completedBlock {
    
    //看名字猜测是取消当前图片加载(任务) 
    [self sd_cancelCurrentImageLoad];
    ...
    
    //使用placeholder图片先占位
    if (!(options & SDWebImageDelayPlaceholder)) {
        dispatch_main_async_safe(^{
            self.image = placeholder;
        });
    }
    ...
    
        //这里就应该是主要的方法 下载图片了
        id <SDWebImageOperation> operation = [SDWebImageManager.sharedManager downloadImageWithURL:url options:options progress:progressBlock completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
             ...
                    wself.image = image;
                    ...
        }];
        
        //看名字猜测将这个图片下载任务以UIImageViewImageLoad作为关键字储存起来
        [self sd_setImageLoadOperation:operation forKey:@"UIImageViewImageLoad"];
        
    ...
    }
    

    2.1 先看[self sd_cancelCurrentImageLoad]

    它是调用了以下代码:

    - (void)sd_cancelCurrentImageLoad {
    [self sd_cancelImageLoadOperationWithKey:@"UIImageViewImageLoad"];
    }
    

    sd_cancelImageLoadOperationWithKey这个方法在 UIView+WebCacheOperation这个分类中:

    - (void)sd_cancelImageLoadOperationWithKey:(NSString *)key {
    // Cancel in progress downloader from queue
    
    //会将operation 都储存在operationDictionary?
    NSMutableDictionary *operationDictionary = [self operationDictionary];
    
    //取出key对应的operation(s) ,并执行cancel操作,然后将operation(s)从operationDictionary中删除
    id operations = [operationDictionary objectForKey:key];
    if (operations) {
        if ([operations isKindOfClass:[NSArray class]]) {
            for (id <SDWebImageOperation> operation in operations) {
                if (operation) {
                    [operation cancel];
                }
            }
        } else if ([operations conformsToProtocol:@protocol(SDWebImageOperation)]){
            [(id<SDWebImageOperation>) operations cancel];
        }
        [operationDictionary removeObjectForKey:key];
    }
    }
    

    也就是将key为UIImageViewImageLoad的操作都执行cancel操作,最后将key对应的对象全部从operationDictionary中删除

    看到这里有几个问题

    UIImageViewImageLoad对应的操作是图片正在下载中,还是下载完呢?应该是下载完的操作,因为要是下载中操作都被取消并从operationDictionary中移除,图片也下不成功了。但是要是对应下载完,operation已经被移除,它是依据什么做到同一url不重复下载??url(或者ur对应的特征码)不储存在operation(但是SDWebImageOperation只是一个协议 只声明了cancel操作)中吗?还有operationDictionary的内部储存数据的结构是什么?怎么一个key又可能是数组有可能是单个对象,这样做不是麻烦一点吗?全改成一个key对应一个operation数组不是更方便?所以问题就这几个:

    • 问题2:UIImageViewImageLoad对应的是什么操作

    • 问题3:operation的实现(SDWebImageOperation中的cancel方法实现 和 内部的属性等)

        @protocol SDWebImageOperation <NSObject>
        - (void)cancel;
        @end 
      
    • 问题4:不重复下载相同url 是根据operation做的,还是根据其他对象实现的

    • 问题5:operationDictionary的内部储存数据(能存什么key,key对应的对象是数组还是其他)

      • 目前知道有个key 为UIImageViewImageLoad ,看代码猜测当Operation数量为多个时,key对应的是NSArray<SDWebImageOperation*> 对象,当数量为单个时key对应的是 SDWebImageOperation*对象(或者是考虑到兼容问题?)

    感觉这几个问题都可以在下载的具体步骤中解决掉。。

    2.2 接下来看 dispatch_main_async_safe

    它的实现就是一个宏:

    #define dispatch_main_async_safe(block)
    if ([NSThread isMainThread]) {
        block();
    } else {
        dispatch_async(dispatch_get_main_queue(), block);
    }
    

    这个我有个蠢问题,为什么要这么做??就算是已经在主线程 但是再执行dispatch_async(dispatch_get_main_queue(), block)也不会错吧??虽然是会感觉多此一举,但是这样做其他的影响呢,会影响性能吗。。

    2.3 [self showActivityIndicatorView]

    我加上 [cell.imageView setShowActivityIndicatorView:true];也没有看到等待指示器。。是网速太快了还是需要其他设置。。不管了

    2.4 [self sd_setImageLoadOperation:operation forKey:@"UIImageViewImageLoad"];

    看下它的代码:

    - (void)sd_setImageLoadOperation:(id)operation forKey:(NSString *)key {
    [self sd_cancelImageLoadOperationWithKey:key];
    NSMutableDictionary *operationDictionary = [self operationDictionary];
    [operationDictionary setObject:operation forKey:key];
    }
    

    它将Operation以key为 UIImageViewImageLoad 存入operationDictionary,而这时Operation对应的下载肯定是异步的,所以UIImageViewImageLoad对应的是图片正在下载中的操作。

    • 回答问题2:UIImageViewImageLoad对应的是图片正在下载中的操作。
    • 回答问题5(部分):operationDictionary中以 key-operation 方式储存(key-nsarray方式暂时没看到)

    若是当第一个图片没下载完,第二图片下载任务进来不就取消之前所有的UIImageViewImageLoad的operation了??那之前的图片怎么下载完?难道cancel之后会自动resume吗?

    • 问题6:UIImageViewImageLoad的operation执行 cancel后,在哪里会继续下载?

    3 接下来就是内容最多的下载功能了 [SDWebImageManager.sharedManager downloadImageWithURL: l options: progress: completed:

  • 相关阅读:
    简单工厂模式
    工厂方法模式
    Linq对DatatTable进行分组统计
    微软现在支持某一路径下?占位符的文件扫描
    Spring MVC中InternalResourceViewResolver视图解析器的默认行为
    什么是 HandlerMethod ?
    03基于NIO的聊天室案例
    01基于BIO的多人聊天室
    02基于BIO的多线程客户端服务器通信案例
    java8新特性学习
  • 原文地址:https://www.cnblogs.com/sunyanyan/p/5333937.html
Copyright © 2020-2023  润新知