【问题标题】:Crashes with UITableViewUITableView 崩溃
【发布时间】:2023-03-14 03:05:02
【问题描述】:

我在 UITableView 中显示一个数组对象,其中包含插入、更新和删除,即使我正在仔细处理更改,偶尔也会出现以下异常。我在 GitHub 上有完整的代码示例。

https://github.com/brennanMKE/TableMaddness

此示例项目使用实现 NSCoding 和 NSCopying 以及 isEqual 的对象,以便使用插入、更新和删除来更新表可以模拟我在具有相同问题的真实应用程序中所做的事情。我正在避免使用会使用 NSFetchedResultsController 的 Core Data,所以我想知道如果我不使用 Core Data 是否应该使用某些东西。

断言失败 -[UITableView _endCellAnimationsWithContext:], /SourceCache/UIKit_Sim/UIKit-2903.23/UITableView.m:1330 2013-11-25 14:43:20.217 TableMaddness [13411:70b] 由于未捕获的异常“NSInternalInconsistencyException”而终止应用程序,原因:“无效更新:第 0 节中的行数无效。之后现有节中包含的行数更新 (4) 必须等于更新 (4) 之前该节中包含的行数,加上或减去从该节中插入或删除的行数(插入 1,删除 0)加上或减去数字移入或移出该部分的行数(0 移入,0 移出)。'

下面是相关代码。

    // determine items which need to be inserted, updated or removed
    NSMutableArray *inserts = [@[] mutableCopy];
    NSMutableArray *deletes = [@[] mutableCopy];
    NSMutableArray *reloads = [@[] mutableCopy];

    // look for inserts
    for (NSUInteger row=0; row<fetchedItems.count; row++) {
        SSTItem *item = fetchedItems[row];
        if (![self.currentItems containsObject:item]) {
            // inserts are items which are not already in self.items
            [inserts addObject:[NSIndexPath indexPathForRow:row inSection:0]];
        }
        else {
            NSUInteger otherIndex = [self.currentItems indexOfObject:item];
            SSTItem *otherItem = [self.currentItems objectAtIndex:otherIndex];
            if (![item.modified isEqualToDate:otherItem.modified]) {
                [reloads addObject:[NSIndexPath indexPathForRow:row inSection:0]];
            }
        }
    }

    // look for deletes
    for (NSUInteger row=0; row<self.currentItems.count; row++) {
        SSTItem *item = self.currentItems[row];
        if (![fetchedItems containsObject:item]) {
            [deletes addObject:[NSIndexPath indexPathForRow:row inSection:0]];
        }
    }

    static NSString *lock = @"LOCK";

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (kDelay / 4) * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
        // lock is required to prevent inconsistencies when changing view orientation during rotation
        @synchronized(lock) {
            self.currentItems = fetchedItems;

            NSUInteger numberOfRowsInSection = [self tableView:self.tableView numberOfRowsInSection:0];
            DebugLog(@"numberOfRowsInSection: %li", numberOfRowsInSection);
            DebugLog(@"self.items: %li", self.currentItems.count);

            MAAssert(self.currentItems.count == numberOfRowsInSection, @"Match is required");

            if (inserts.count || deletes.count || reloads.count) {
                [self.tableView beginUpdates];
#ifndef NDEBUG
                for (NSIndexPath *indexPath in inserts) {
                    DebugLog(@"Inserting at %li", (long)indexPath.row);
                }
                for (NSIndexPath *indexPath in deletes) {
                    DebugLog(@"Deleting at %li", (long)indexPath.row);
                }
                for (NSIndexPath *indexPath in reloads) {
                    DebugLog(@"Reloading at %li", (long)indexPath.row);
                }
#endif
                [self.tableView insertRowsAtIndexPaths:inserts withRowAnimation:UITableViewRowAnimationAutomatic];
                [self.tableView deleteRowsAtIndexPaths:deletes withRowAnimation:UITableViewRowAnimationAutomatic];
                [self.tableView reloadRowsAtIndexPaths:reloads withRowAnimation:UITableViewRowAnimationAutomatic];
                [self.tableView endUpdates];
            }
        }

        index++;
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, kDelay * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
            [self runNextUpdate];
        });
    });

【问题讨论】:

  • 在调用 beginUpdates 并更改表之前,您需要更新表的数据源以反映新的行集。
  • @rmaddy 此代码将 self.currentItems 设置为同步块中的 fetchedItems。我在下面选择的答案可以防止异常,尽管我认为同步块可以防止该问题。至少现在我知道这个问题是由于一次运行多个更新造成的。

标签: ios objective-c uitableview


【解决方案1】:
- (void)runNextUpdate {
if (index >= self.dataSets.count) {
    index = 0;

    [self runNextUpdate]; // remove this line
    return;               // or add this line.
}

因为如果这个条件为真,你的代码会几乎同时运行两次,但是数据源并没有更新。

【讨论】:

  • 谢谢,这停止了异常,尽管它仍然使用同步块,它应该防止 2 个更新同时运行。也许 GCD 会阻止它按我的预期工作。在该同步块中,currentItems 使用 beginUpdates/endUpdates 序列更新。在真正的应用程序中,我将不得不采取措施来处理这种情况,因为提取可以随时返回。感谢您的帮助!
  • 不客气(:感谢您接受答案。是的,我认为您应该使用GCD而不是@synchronized,例如使用dispatch_group。这篇文章可能对您有所帮助:jamiepinkham.com/post/9046964416/…
猜你喜欢
  • 2018-07-15
  • 1970-01-01
  • 1970-01-01
  • 2011-08-19
  • 2011-08-08
  • 1970-01-01
  • 1970-01-01
  • 2011-09-27
相关资源
最近更新 更多