美文网首页
SDWebImageView 源码分析2:UIView+WebC

SDWebImageView 源码分析2:UIView+WebC

作者: dc630f46ee2d | 来源:发表于2017-10-29 19:19 被阅读0次

    前言

    上一篇中分析了实际上UIImageView+WebCache调用的是

    [self sd_internalSetImageWithURL:url 
                        placeholderImage:placeholder
                                 options:0
                            operationKey:nil
                           setImageBlock:nil
                                progress:nil
                               completed:nil];
    
    

    将它的实现方法分为4部分

    - (void)sd_internalSetImageWithURL:(nullable NSURL *)url
                      placeholderImage:(nullable UIImage *)placeholder
                               options:(SDWebImageOptions)options
                          operationKey:(nullable NSString *)operationKey
                         setImageBlock:(nullable SDSetImageBlock)setImageBlock
                              progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
                             completed:(nullable SDExternalCompletionBlock)completedBlock {
        // 1.
        NSString *validOperationKey = NSStringFromClass([self class]);
        [self sd_cancelImageLoadOperationWithKey:validOperationKey];
       // 2.
        objc_setAssociatedObject(self, &imageURLKey, url, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
        
         //3.
        if (!(options & SDWebImageDelayPlaceholder)) {
            dispatch_main_async_safe(^{
                [self sd_setImage:placeholder imageData:nil basedOnClassOrViaCustomSetImageBlock:setImageBlock];
            });
        }
        
       // 4.
        if (url) {
            
            __weak __typeof(self)wself = self;
            id <SDWebImageOperation> operation = [SDWebImageManager.sharedManager loadImageWithURL:url options:options progress:progressBlock completed:^(UIImage *image, NSData *data, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
                __strong __typeof (wself) sself = wself;
                if (!sself) {
                    return;
                }
                dispatch_main_async_safe(^{
                    if (!sself) {
                        return;
                    }
                    
                    if (image) {
                        [sself sd_setImage:image imageData:data basedOnClassOrViaCustomSetImageBlock:setImageBlock];
                        [sself sd_setNeedsLayout];
                    } else {
                        if ((options & SDWebImageDelayPlaceholder)) {
                            [sself sd_setImage:placeholder imageData:nil basedOnClassOrViaCustomSetImageBlock:setImageBlock];
                            [sself sd_setNeedsLayout];
                        }
                    }
                });
            }];
            [self sd_setImageLoadOperation:operation forKey:validOperationKey];
        } 
    }
    

    1. 取消正在进行的loading操作

     NSString *validOperationKey = NSStringFromClass([self class]); // part1
     [self sd_cancelImageLoadOperationWithKey:validOperationKey];// part2
    

    part1. 其中validOperationKey在当前的例子中就是@"UIImageView",因此
    NSString *validOperationKey = @"UIImageView";

    2. 为这个对象绑定一个属性,key 名为imageURLKey,value就是我们传入的网址url

    objc_setAssociatedObject(self, &imageURLKey, url, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

    3. 如果设置的Options值不包含SDWebImageDelayPlaceholder,那么先将imageView的image设置为placeHolder

        if (!(options & SDWebImageDelayPlaceholder)) { // a.
            dispatch_main_async_safe(^{ // b.
                [self sd_setImage:placeholder imageData:nil basedOnClassOrViaCustomSetImageBlock:setImageBlock]; // c.
            });
        }
    

    简单分析一下
    a. options & SDWebImageDelayPlaceholder 表示options为NS_OPTIONS 枚举变量是否包含SDWebImageDelayPlaceholder值,如果是返回 True,否则返回False.显然我们这里返回False,取反后为True.

    b. dispatch_main_async_safe()是一个宏函数

    #ifndef dispatch_main_async_safe
    #define dispatch_main_async_safe(block)\
    if (strcmp(dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL), dispatch_queue_get_label(dispatch_get_main_queue())) == 0) {\
            block();\
        } else {\
            dispatch_async(dispatch_get_main_queue(), block);\
        }
    #endif
    

    如果当前线程是主线程 ,调用 [self sd_setImage:placeholder imageData:nil basedOnClassOrViaCustomSetImageBlock:setImageBlock];在主线程上,直接同步顺序执行,否则异步提交到主线程上执行。因为在主线程上异步提交一个任务到主线程上会引起随机的崩溃。

    c. [self sd_setImage:placeholder imageData:nil basedOnClassOrViaCustomSetImageBlock:setImageBlock]对应的方法实现如下

    - (void)sd_setImage:(UIImage *)image imageData:(NSData *)imageData basedOnClassOrViaCustomSetImageBlock:(SDSetImageBlock)setImageBlock {
        if (setImageBlock) {
            setImageBlock(image, imageData);
            return;
        }
        
    #if SD_UIKIT || SD_MAC
        if ([self isKindOfClass:[UIImageView class]]) {
            UIImageView *imageView = (UIImageView *)self;
            imageView.image = image;
        }
    #endif
        
    #if SD_UIKIT
        if ([self isKindOfClass:[UIButton class]]) {
            UIButton *button = (UIButton *)self;
            [button setImage:image forState:UIControlStateNormal];
        }
    #endif
    }
    

    在我们的代码调用中,setImageBlock为空,所以 下面代码不进行任何操作

        if (setImageBlock) {
            setImageBlock(image, imageData);
            return;
        }
    

    SD_UIKIT ,SD_MAC两个简单的宏,前者表示在运行的设备是iOS设备或者是iwatch.后者表示平台是mac平台,对应的定义在SDWebImageCompat文件中。如下

    #if !TARGET_OS_IPHONE && !TARGET_OS_IOS && !TARGET_OS_TV && !TARGET_OS_WATCH
        #define SD_MAC 1
    #else
        #define SD_MAC 0
    #endif
    
    // iOS and tvOS are very similar, UIKit exists on both platforms
    // Note: watchOS also has UIKit, but it's very limited
    #if TARGET_OS_IOS || TARGET_OS_TV
        #define SD_UIKIT 1
    #else
        #define SD_UIKIT 0
    #endif
    

    所以翻译一下,如果selfUIImageView就设置imageView.image = image,如果是iOS平台设置UIbuttonnormal状态的image

    4.调用SDWebImageManager.sharedManager的下载方法在回调中处理返回的数据

    a.我们先研究一下回调做了些什么事情

    {
      __strong __typeof (wself) sself = wself;
                if (!sself) {
                    return;
                }
                dispatch_main_async_safe(^{
                    if (!sself) {
                        return;
                    }
                    
                    if (image) {
                        [sself sd_setImage:image imageData:data basedOnClassOrViaCustomSetImageBlock:setImageBlock];
                        [sself sd_setNeedsLayout];
                    } else {
                        if ((options & SDWebImageDelayPlaceholder)) {
                            [sself sd_setImage:placeholder imageData:nil basedOnClassOrViaCustomSetImageBlock:setImageBlock];
                            [sself sd_setNeedsLayout];
                        }
                    }
                });
            }
    

    如果当前对象已经被释放,那么不执行任何操作,如果获取到图片,那么设置图片当imageView.image上。如果没有且options有SDWebImageDelayPlaceholder值,那么用placeHolder图片设置最终显示的imageview.image.

    b. 最后一个方法 [self sd_setImageLoadOperation:operation forKey:validOperationKey];实现如下

    - (void)sd_setImageLoadOperation:(nullable id)operation forKey:(nullable NSString *)key {
        if (key) {
            [self sd_cancelImageLoadOperationWithKey:key];
            if (operation) {
                SDOperationsDictionary *operationDictionary = [self operationDictionary];
                operationDictionary[key] = operation;
            }
        }
    }
    
    - (SDOperationsDictionary *)operationDictionary {
        SDOperationsDictionary *operations = objc_getAssociatedObject(self, &loadOperationKey);
        if (operations) {
            return operations;
        }
        operations = [NSMutableDictionary dictionary];
        objc_setAssociatedObject(self, &loadOperationKey, operations, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
        return operations;
    }
    

    上面两个方法都是UIView+WebCacheOperation的方法
    做了两件事情,1.是取消当前正在下载的操作 2.设置保存一个键值对在loadOperationKey对应的字典中。
    key就是@"UIImageView",value是id <SDWebImageOperation>类型的对象

    后续

    后续我们顺着这个思路研究一下SDWebImageManager.sharedManager的下载方法- (id <SDWebImageOperation>)loadImageWithURL:(nullable NSURL *)url options:(SDWebImageOptions)options progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock completed:(nullable SDInternalCompletionBlock)completedBlock做了什么事情

    相关文章

      网友评论

          本文标题:SDWebImageView 源码分析2:UIView+WebC

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