iOS:更新 tableViewCell 无限循环

Posted

技术标签:

【中文标题】iOS:更新 tableViewCell 无限循环【英文标题】:iOS: Updating tableViewCell infinite loop 【发布时间】:2013-03-20 15:10:39 【问题描述】:

这就是问题所在:我在保存并获取 Core Data 的缩略图后更新了我的 tableview,然后我告诉单元格进行自我更新 - 这样当图像加载到 Core Data 中时它可以显示一个缩略图。我使用两个不同的线程,因为 Core Data 不是线程安全的,所有 GUI 元素当然都需要在主线程中发生。

但是这整个方法只是永远循环,而导致它的原因是当我重新加载线程时:

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

为什么?我该如何解决这个问题?

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

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Photo"];
    Photo *photo = [self.fetchedResultsController objectAtIndexPath:indexPath];    
    cell.textLabel.text = photo.title;
    cell.detailTextLabel.text = photo.subtitle;

    NSLog(@"Context %@", self.photographer.managedObjectContext);
    [self.photographer.managedObjectContext performBlock:^
        [Photo setThumbnailForPhoto:photo];
        dispatch_async(dispatch_get_main_queue(), ^
            cell.imageView.image = [UIImage imageWithData:photo.thumbnail];
            [self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
        );
    ];
    return cell;

【问题讨论】:

不要,我重复一遍,不要在 cellforrowatindexpath 中调用重新加载表(单元格)函数。因为它会一遍又一遍地获取图像。当您重新加载 tableview 或任何单元格时,它将再次调用 cellforrowatindexpath(在您的情况下一次又一次) cellForRowAtIndexPath 将在绘制单元格之前被调用。所以它已经更新了。为什么需要在 cellForRowAtIndexPath 中重新加载? Mert:我只是想在那里重新加载它,因为我可以直接在那里访问 tableViewCell。 【参考方案1】:

无限循环是由于在加载fetch并调用某个单元格上的reload之后再次调用cellForRowAtIndexPath:引起的。

重新加载将迫使 cellForRowAtIndexPath: 再次被调用...在你的情况下一次又一次地调用到无穷大。

解决方案很简单...不要在 cellForRowAtIndexPath 中重新加载您的单元格,而是在 fetchrequest 的回调方法中重新加载。然后在那里重新加载它而不是创建单元格。

根本不要在cellForRowAtIndexpath: 中加载图像。 每当您的表被实例化时,请创建一个循环您的数据源并获取每个项目的相应单元格的方法。然后为您认为需要的每个单元格加载图像。并在获取项目完成时重新加载单元格(例如回调方法)。

如果您确实希望像现在这样在创建单元格时加载图像(尽管我认为这不是正确的方法)。你可以用 if 语句包围整个 performBlock: 来检查图像是否已经设置。

【讨论】:

听起来不错!但是...我刚刚意识到我不知道回调方法是什么... 回调方法是在您的 fetchresult 完成时调用的方法。在这种情况下,获取图像。问题是,您需要在 cellForRowIndexpath 之外加载图像 他已经将异步调度到主队列作为回调。 没错,例如,您是否执行了回调方法。或者只是重新加载我认为不正确的代码。主要的是,当图像的加载在 cellForRowAtIndexpath 之外完成时,问题将得到解决:(这就是我一遍又一遍的意思) 但是如果他所做的只是用方法替换调度,他仍然会无限地调用这个(尽管我可能不知道 NSmanagedObjectContext performBlock 是如何工作的)【参考方案2】:

正如其他人所说,加载图像时应该不需要调用reloadRowsAtIndexPaths。为cell.imageView.image 分配一个新图像就足够了。

但是您的代码还有另一个问题。我假设self.photographer.managedObjectContext 是“私有并发类型”的托管对象上下文,因此performBlock 在后台线程上执行。 (否则就没有必要使用dispatch_async(dispatch_get_main_queue(), ...)。)

所以performBlock: 在后台线程上异步执行代码。 (这很好,因为 UI 没有被阻塞。)但是当图像在一段时间后被提取时,单元格可能已重用用于不同的行。 (如果您滚动表格视图以使该行在获取图像时不可见,则会发生这种情况。)

因此,您必须检查单元格是否仍在表格视图中的相同位置:

[self.photographer.managedObjectContext performBlock:^
    [Photo setThumbnailForPhoto:photo];
    dispatch_async(dispatch_get_main_queue(), ^
        if ([[tableView indexPathForCell:cell] isEqualTo:indexPath]) 
            cell.imageView.image = [UIImage imageWithData:photo.thumbnail];
        
    );
];

【讨论】:

【参考方案3】:

我将替换以下代码:

    cell.imageView.image = [UIImage imageWithData:photo.thumbnail];
    [self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];

与:

    UITableViewCell *blockCell = [tableView cellForRowAtIndexPath:indexPath];
    blockCell.imageView.image = [UIImage imageWithData:photo.thumbnail];
    [blockCell setNeedsLayout];

这解决了递归重新加载的问题,以及cell 在此期间被重用的可能性。我还会检查您是否已经加载了照片数据,如果是,则不更新。

由于您使用的是NSFetchedResultsController,如果照片数据更新时委托回调已经通知您,您最好选择 Totomus Maximus 的回答。这种方式只会更新图像视图,不会更新照片更新方法可能更改的任何其他信息。

【讨论】:

以上是关于iOS:更新 tableViewCell 无限循环的主要内容,如果未能解决你的问题,请参考以下文章

iOS_12_tableViewCell的删除更新_红楼梦

如何可视化vue无限更新循环的无限循环

如何在无限循环中不断更新活动中的 TextView?

组件渲染函数中的Vue无限更新循环

盖茨比:上下文更新导致无限渲染循环

在使用游标时,使用 Oracle SQL 更新记录会导致无限循环