python tableview刚开始没有数据很丑_一个TableView(并没有)循环刷新的现象与正确的做法...

在一次CodeReview中,我们发现在一个普通的列表加载高度不定的图片的时候,有如下写法:

- 初始化高度固定为默认值。reloadData。

- 在cellForRow中通过sd加载图片。

- 在图片加载成功后,获取图片高度并塞到本地缓存。

- 再刷新当前的cell。

代码看起来如下:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

VCCell *cell = [tableView dequeueReusableCellWithIdentifier:@"VCCell"];

[cell.img sd_setImageWithURL:[NSURL URLWithString:@"https://p0.meituan.net/klcmspic/616bb9a1d1b17226a75682cf155718c293474.png"] completed:^(UIImage * _Nullable image, NSError * _Nullable error, SDImageCacheType cacheType, NSURL * _Nullable imageURL) {

if (image) {

self.imgHeight = image.size.height;//记录高度缓存

[tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];//刷新当前cell

}

}];

return cell;

}

这个写法咋看起来挺自然流畅的,但是仔细想想就发现不对:reloadRowsAtIndexPaths会让当前cell重新执行cellForRow,难道这个代码不会导致无限循环刷新么?不过事实上确实没有。

Log分析

要分析是否真的循环了,我们只需要插桩一些log就好:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

VCCell *cell = [tableView dequeueReusableCellWithIdentifier:@"VCCell"];

NSInteger flag = arc4random() % 100;//生成当前cellForRow的标记

NSLog(@"Call %ld cellForRowAtIndexPath", flag);//执行cellForRow log

[cell.img sd_setImageWithURL:[NSURL URLWithString:@"https://p0.meituan.net/xxxx/616bb9a1d1b17226a75682cf155718c293474.png"] completed:^(UIImage * _Nullable image, NSError * _Nullable error, SDImageCacheType cacheType, NSURL * _Nullable imageURL) {

if (image) {

self.imgHeight = image.size.height;

NSLog(@"Call %ld reloadRow", flag);//开始reloadRows log

[tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];

NSLog(@"Call %ld reloadRow end", flag);//结束reloadRows log

}

}];

return cell;

}

执行的结果如下:

play[16969:1820508] Call 36 cellForRowAtIndexPath

play[16969:1820508] Call 36 reloadRow

play[16969:1820508] Call 13 cellForRowAtIndexPath

play[16969:1820508] Call 13 reloadRow

play[16969:1820508] Call 13 reloadRow end

play[16969:1820508] Call 36 reloadRow end

//结束

咋看起来和我们预期的似乎是一样的,又没那么一样。我们可以得到的信息包括:

- cellForRowAtIndexPath确实执行了第二次,但是第二次却没有触发第三次的执行

- 第二次cellForRowAtIndexPath与后续操作是在第一次reloadRow操作过程中同步完成的,即第二条log到第六条log是同步代码

似乎明白了:

- reloadRowsAtIndexPaths方法是同步执行的(包括使用[tableView beginUpdates]/[tableView endUpdates]包着的情况也是同步执行的)。因此36 reloadRow直接调用到13 cellForRowAtIndexPath了。

- 第二次sd_setImageWithURL一定是同步执行的:想想也就明白了,这里是第二次触发同一个地址的sd_setImageWithURL了,图像一定已经在本地有缓存了。在有缓存的时候,sd确实是同步返回图片并且执行callback。

- 似乎,因为reloadRowsAtIndexPaths这个方法是同步执行的缘故,reloadRowsAtIndexPaths的过程中,再次调用reloadRowsAtIndexPaths是无效的,这导致了没有触发第三次循环。

为了验证这个结果,我们依次尝试了三个方法:reloadRowsAtIndexPaths,reloadSections,reloadData。结果上看:

- reloadRowsAtIndexPaths和reloadSections会立刻同步执行,并且这个case下确实不会触发第三次刷新。

- reloadData会在下一次tick执行,因此如果使用reloadData的话,这里就无限循环了。(巧妙的是,因为延迟到下一次tick才执行,因此还没有卡死。。。)

- 同时,也确定了[tableView beginUpdates]/[tableView endUpdates]对以上结果并没有影响。

因此,结论上,这个写法之所以能正常执行,是sd缓存图片同步返回,和reloadRowsAtIndexPaths方法会同步执行,执行时重复触发无效,这几个特性,瞎猫碰见死耗子凑成了。

正确的写法

虽然瞎猫碰见死耗子凑成了,但是事实上可以看见cellForRowAtIndexPath执行了两次,符合预期,sd_setImageWithURL执行了两次就没有必要了。

因此,从开发的角度,应该需要在加载图片之前和执行刷新之前,多次校验和场景判断,从而避免有没有必要的代码重复执行,最大化提升代码执行效率:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

VCCell *cell = [tableView dequeueReusableCellWithIdentifier:@"VCCell"];

if (cell.img.sd_imageURL != self.dataImgUrl) {//imageview校验,如果cell是复用的,接下来就没了

[cell.img sd_setImageWithURL:self.dataImgUrl completed:^(UIImage * _Nullable image, NSError * _Nullable error, SDImageCacheType cacheType, NSURL * _Nullable imageURL) {

if (image) {

if (self.imgHeight < 0.0) {//高度缓存储存校验,一定会挡在这里来避免重复刷新

self.imgHeight = image.size.height;

[tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];

}

}

}];

}

return cell;

}

而从正确的设计上考虑。我们刷新cell的目的是因为加载了图片后cell的高度发生改变。基于数据驱动原则:

1. sd_setImageWithURL:self回调中仅触发高度缓存刷新。

2. 监听高度缓存数组,对高度发生变化的cell执行reload。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值