【发布时间】: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