reloadRowsAtIndexPaths:withRowAnimation: 使我的应用程序崩溃

Posted

技术标签:

【中文标题】reloadRowsAtIndexPaths:withRowAnimation: 使我的应用程序崩溃【英文标题】:reloadRowsAtIndexPaths:withRowAnimation: crashes my app 【发布时间】:2011-05-26 13:53:27 【问题描述】:

我的 UITableView 出现了一个奇怪的问题:我使用 reloadRowsAtIndexPaths:withRowAnimation: 重新加载了一些特定的行,但应用程序崩溃并出现看似无关的异常:NSInternalInconsistencyException - 尝试删除的行多于部分中存在的行。 em>

我的代码如下:

[self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:0 inSection:0]] withRowAnimation:UITableViewRowAnimationFade];

当我用简单的reloadData 替换该reloadRowsAtIndexPaths:withRowAnimation: 消息时,它可以完美运行。

有什么想法吗?

【问题讨论】:

检查类似问题:***.com/questions/3542260/… 可能会有所帮助 【参考方案1】:

问题是您可能更改了UITableView 数据源的项目数。例如,您在实现UITableViewDataSource 协议时使用的数组或字典中添加或删除了一些元素。

在这种情况下,当您调用reloadData 时,您的UITableView 会完全重新加载,包括部分数和行数。

但是当您调用reloadRowsAtIndexPaths:withRowAnimation: 时,这些参数不会重新加载。这导致了下一个问题:当您尝试重新加载某个单元格时,UITableView 检查数据源的大小并发现它已被更改。这会导致崩溃。只有当你想重新加载单元格的内容视图时(例如,标签已经改变或者你想改变它的大小),才可以使用此方法。

现在,如果您想从UITableView 中删除/添加单元格,您应该使用下一种方法:

    通过调用方法beginUpdates通知UITableView其大小将被改变。 通知使用方法- (void)insertRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation插入新行。 通知使用方法- (void)deleteRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation 删除行。 通过调用方法endUpdates,通知UITableView其大小已更改。

【讨论】:

理论上说得通,但行不通。我正在尝试重新加载部分,但它仍然崩溃。我使用 UITableViewAutomaticDimension 的估计标题和单元格高度作为高度,这是一个要求。 @sheetal,如果您采用此答案中的方法,则不必调用 reloadRowsAtIndexPaths:withRowAnimation:。基本上在模型数据结构中添加一行(大多数情况下是一个数组)。然后调用 beginUpdate、insertRowsAtIndexPaths 和 endUpdates。【参考方案2】:

我认为以下代码可能有效:

[self.tableView beginUpdates];

[self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:0 inSection:0]] withRowAnimation:UITableViewRowAnimationFade];

[self.tableView endUpdates];

【讨论】:

这个解决了我的问题。我正在使用核心数据,并且我已将不同的提取请求传递给 resultfetchcontroller 并在提取完成后重新加载表。即使提取成功,应用程序也崩溃了.但是使用上述方法重新加载成功。 不正确。如果只有一个 reloadRowsAtIndexPath,则不需要 beginUpdates/endUpdates,但如果这是尝试重新加载新添加的行,则永远不会修复崩溃。详情请查看 Nekto 的回答。【参考方案3】:

我遇到了这个问题,这是由调用 reloadRowsAtIndexPaths:withRowAnimation: 的块和调用 reloadData 的并行线程引起的。 崩溃是由于 reloadRowsAtIndexPaths:withRowAnimation 找到了一个空表,即使我检查了 numberOfRowsInSection 和 numberOfSections。

我的态度是,我真的不在乎它是否会导致异常。作为应用程序的用户,我可以忍受视觉损坏而不是整个应用程序崩溃。

这是我的解决方案,我很乐意分享并欢迎建设性的批评。如果有更好的解决方案,我很想听听?

- (void) safeCellUpdate: (NSUInteger) section withRow : (NSUInteger) row 
    // It's important to invoke reloadRowsAtIndexPaths implementation on main thread, as it wont work on non-UI thread
    dispatch_async(dispatch_get_main_queue(), ^
        NSUInteger lastSection = [self.tableView numberOfSections];
        if (lastSection == 0) 
            return;
        
        lastSection -= 1;
        if (section > lastSection) 
            return;
        
        NSUInteger lastRowNumber = [self.tableView numberOfRowsInSection:section];
        if (lastRowNumber == 0) 
            return;
        
        lastRowNumber -= 1;
        if (row > lastRowNumber) 
            return;
        
        NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:section];
        @try 
            if ([[self.tableView indexPathsForVisibleRows] indexOfObject:indexPath] == NSNotFound) 
                // Cells not visible can be ignored
                return;
            
            [self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
        

        @catch ( NSException *e ) 
            // Don't really care if it doesn't work.
            // It's just to refresh the view and if an exception occurs it's most likely that that is what's happening in parallel.
            // Nothing needs done
            return;
        
    );

【讨论】:

【参考方案4】:

经过多次尝试,我发现“reloadRowsAtIndexPaths”只能在某些地方使用,如果只更改单元格内容而不是插入或删除单元格。不是任何地方都可以用的,就算你把它包起来

[self beginUpdates];
//reloadRowsAtIndexPaths
[self endUpdates];

我发现可以使用的地方有:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath - (IBAction) unwindToMealList: (UIStoryboardSegue *) sender

任何从其他地方尝试,例如从“viewDidLoad”或“viewDidAppear”调用它,要么不会生效(对于已经加载的单元格,我的意思是,重新加载不会生效)或导致异常。

所以尽量只在那些地方使用“reloadRowsAtIndexPaths”。

【讨论】:

【参考方案5】:

您应该在重新加载之前检查单元格的可见性。这是 Swift 3 代码:

        let indexPath = IndexPath(row: offset, section: 0)
        let isVisible = tableView.indexPathsForVisibleRows?.contains$0 == indexPath
        if let v = isVisible, v == true 
            tableView.reloadRows(at: [indexPath], with: .automatic)
        

【讨论】:

遇到了完全相同的问题,这就是解决方案:如果 indexPath 不可见,我将改用 reloadData【参考方案6】:

我有同样的问题。就我而言;只有当另一个视图控制器弹出/推送到现有的表视图控制器然后调用 [self.tableView reloadRowsAtIndexPaths] 函数时才会发生这种情况。

reloadRowsAtIndexPaths 调用隐藏/显示表视图中的不同行,该表视图具有 30 多个视觉复杂的行。当我尝试解决这个问题时,我发现如果我稍微滚动一下表格视图应用程序并没有崩溃。如果我不隐藏单元格(通过返回 0 作为高度),它也不会崩溃

为了解决这个问题,我只是更改了“(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath”函数并返回至少 0.01 作为行高。

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath

....
return rowModel.height + 0.01; // Add 0.01 to work around the crash issue.

它为我解决了这个问题。

【讨论】:

【参考方案7】:

这是旧的。不使用。 我在调用reloadRowsAtIndexPaths... 以将单元格更改为包含UITextField 的编辑单元格时遇到了这个问题。该错误告诉我我正在删除表中的所有行。为了解决这个问题,我删除了:

[self.tableView beginUpdates];
NSArray *reloadIndexPath = [NSArray arrayWithObject:[NSIndexPath indexPathForRow:count inSection:section]];
[self.tableView reloadRowsAtIndexPaths:reloadIndexPath withRowAnimation:UITableViewRowAnimationFade];  
[self.tableView endUpdates];

替换为

[self.tableView reloadData];

【讨论】:

【参考方案8】:

应用程序崩溃是因为您对 tableView 进行了一些更改。您已经向 tableView 添加或删除了一些行。因此,当视图控制器向您的模型控制器类询问数据时,indexPaths 中存在不匹配。由于 indexPaths 修改后发生了变化。

所以要么你简单地删除调用

[self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:0 inSection:0]] withRowAnimation:UITableViewRowAnimationFade];

或替换为

[self.tableView reloadData];

调用 reloadData 会检查您的节数、每个节中的行数,然后重新加载整个内容。

【讨论】:

以上是关于reloadRowsAtIndexPaths:withRowAnimation: 使我的应用程序崩溃的主要内容,如果未能解决你的问题,请参考以下文章