iOS:更新tableViewCell无限循环

时间:2013-03-20 15:10:40

标签: ios objective-c multithreading core-data uitableview

这是问题:我在保存并将缩略图提取到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;
}

3 个答案:

答案 0 :(得分:3)

在加载fetch并调用某个单元格的重载后,再次调用cellForRowAtIndexPath:会导致无限循环。

重新加载会强制再次调用cellForRowAtIndexPath: ...并且在您的情况下一次又一次地调用无穷大。

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

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

如果您确实希望像现在一样在图像的创建中加载图像(虽然我认为这不是正确的方法)。您可以使用if语句检查整个performBlock:是否已设置图像。

答案 1 :(得分:1)

正如其他人已经说过的那样,在加载图像时不需要调用reloadRowsAtIndexPaths。将新图像分配给cell.imageView.image就足够了。

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

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

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

[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];
        }
    });
}];

答案 2 :(得分:1)

我将替换以下代码:

    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的回答。这种方式只是更新图像视图,不会更新Photo更新方法可能更改的任何其他信息。