美文网首页
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