zoukankan      html  css  js  c++  java
  • 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:

  • 相关阅读:
    mysq 日期相减
    说说时间观与时间管理——北漂18年(71)
    ionic之切换开关
    ionic之单选框
    SELECT ... LOCK IN SHARE MODE和SELECT ... FOR UPDATE locks在RR模式下可以看到最新的记录
    14.5.2.3 Consistent Nonlocking Reads 一致性非锁定读
    14.5.2.2 autocommit, Commit, and Rollback
    14.5.2 事务隔离级别
    对于唯一索引使用唯一条件搜索, InnoDB 只锁定找到的index record,不是它之前的区间
    mysql explain 解释
  • 原文地址:https://www.cnblogs.com/sunyanyan/p/5333937.html
Copyright © 2011-2022 走看看