tableviewcell划走了图片显示吗

写在最前面

大家都很忙, 如果你没时间看下面一长串的文字, 这里我尽量长话短说, 希望能节约一些你的时间

前提知识 
1 每一个UIView+WebCache 都有一个operationDictionary 里面装的是 @{你当前的类名 : Operation}. 
2 Operation直接影响图片能否被显示.每次有新的加载任务时都会检查这个字典, key相同则移除Operation, 再添加新的Operation, 下载完成后会移除Operation.

下面以tableViewcell 为例 
1 tableViewCell第一次出现,以UIImageView为例 operationDictionary里装的是 @{@”UIImageView” : 第一张图的Operation} 
2 这时候tableView上下滑动, cell复用, UIImageView去加载第二张图片(这就开始复用了), 
3 这时候检查operationDictionary, 如果第一张图片还没有完成回调, key一定还存在operationDictionary中. 如果key重复了, 则移除第一张图的Operation, 并添加第二张图的Operation 即@{@”UIImageView” : 第二张图的Operation}. 
4 候第一张图片下载完成, 准备回调时, 发现第一张图的Operation已经没有了, 就不会回调. 
5 第二张图下载完成后, 会检查第二张图片的Operation有没有被第三张, 第四张…..移除. 
6 如果图片对应的Operation存在 就调用回调方法,显示图片, 否则不调用回调, 也就不会显示.

由此 SDWebImage 就做到了 避免复用.

注意点 
1. 我说的第一张图片,第二张图片,指的是同一个UIImageView要复用的图片, 不是tableView 上显示的第一张,第二张图片. 
2. UIView+WebCache 是一个分类, 每一个对象都有自己的 operationDictionary.

当然如果你想具体了解, 可以往下看.

前言:

SDWebImage是iOS开发最常见的开源框架之一, 对SDWebImage的讲解网上非常多, 我在这里就不放传送门了。 在tableView上使用SDWebImage加载图片时能很好的避免复用问题, 那么他是怎么做到的呢? 自己研究了一下, 下面与大家分享。

前提

在看下面之前, 我认为大家已经看过SDWebimage源码, 这里只会对避免复用这块逻辑讲解, 不对其他逻辑做太多解释。 如果看官还没有看过源码, 建议先上网搜下详解, 以免一会有不适感。

正文

第一个问题, tableViewCell 上下滚动, ImageView被复用了但是之前加载的图片没有显示出来, 说明了什么? 
对的, 说明之前的操作被取消了对吗? 那么怎么被取消的呢? 第一反应就是回调的block被终止了, 对吗? 我跟了一下源码发现取消操作是在SDWebImageManager 里取消的. 请看下图.


经过我的打印发现, if (!strongOperation || strongOperation.isCancelled) 这个判断能进去的原因是strongOperation 是nil 
2. 那么下一个问题strongOperation 在什么时候被置空的? 
如果知道了这个问题, 那么为什么imageView复用为什么不会显示错误的图片的问题 我们就弄清楚了. 
先看下图 


简单说下 UIView+WebCache 调用 SDWebImageManager的方法, 在SDWebImageManager方法里 创建OPeration. Operation去了两个地方, 1. 返回给了 UIView+WebCache. 2. 被抓取到 SDWebImageManager方法内部的Block里了(也就是刚才看到的 取消操作的地方). 
好的下面我们看看 UIView+WebCache. 里做了什么 会让strongOperation 置nil.

2 UIView+WebCache 
请看下图 


关注一下 划线的两个方法 
先讲第二个那个方法 
[self sd_setImageLoadOperation:operation forKey:validOperationKey]; 
这个方法的调用,则是把strongOperation 添加到字典里的方法.

查看源码我们可以看到

- (void)sd_setImageLoadOperation:(nullable id)operation forKey:(nullable NSString *)key {
    if (key) {
        // 取消已经存在的key, Operation
        // 重新加入到 Operation字典中
        [self sd_cancelImageLoadOperationWithKey:key];
        if (operation) {
            SDOperationsDictionary *operationDictionary = [self operationDictionary];
            operationDictionary[key] = operation;
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
将operation 添加到operationDictionary字典中

再说第一个那个划线的方法.

    // 如果Operation 字典里 有这个 Operation 则取消 说明这个view 有新的下载任务了.
    [self sd_cancelImageLoadOperationWithKey:validOperationKey];
1
2
这个方法的调用就是我们要找的把strongOperation 置nil的地方. 每次有图片需要加载的时候, 都会走这里, 这个方法会检查根据key能不能取到值,如果能取到, 这将这个 Operation 调用取消方法, 且将他中字典里移除. 
查看源码

- (void)sd_cancelImageLoadOperationWithKey:(nullable NSString *)key {
    // Cancel in progress downloader from queue
    // 如果有 当前key 有所对应的 Operation. 则将Operation 取消.并且将key -value 从 OperationDictiona中移除.
    SDOperationsDictionary *operationDictionary = [self operationDictionary];
    id operations = operationDictionary[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];        
    }else{
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
这段代码其实就是, 在operationDictionary里根据key查找, 如果有则取消, 并且从operationDictionary里面移除.也是导致 strongOperation 是nil的直接原因.

好了, 这就是strongOperation 被置成nil的原因

下面我们关注 这个字典里的key, 因为 strongOperation 是根据key找到到.

4 key 
我们先来看下 key是怎么创建的

   NSString *validOperationKey = operationKey ?: NSStringFromClass([self class]);
    [self sd_cancelImageLoadOperationWithKey:validOperationKey];
1
2
如果我们使用myimageView sd_setImageWithUR:xxxx 这种方式 operationKey是nil, 那么 key 是通过NSStringFromClass([self class]) 创建的.以UIImageView为例 key 就是 @”UIImageView”.

让我们串起来讲一下 
每次有新的加载任务会做两件事 
1 检查operationDictionary有没有相同的key 有则将key对应的Operation 取消并删除, 
2 将新任务的Operation添加到字典中. 加载图片完成后, 回调时会先检查任务的Operation还在不在, 不在了 则不回调显示, 反之回调显示并移除Operation. 
好了, 这就是我理解的SDWebImage 避免复用的方法.

注意点 1. UIView+WebCache是一个分类, 每一个UIImageView都有一个operationDictionary 这里面key发生重复的原因只有一个,就是这个容器上一个加载任务还没完成, 新的加载任务又开始了.这是我一开始没转过弯 理解上卡主的地方.
 

猜你喜欢

转载自blog.csdn.net/ios_xumin/article/details/118086213
今日推荐