我在基于单元格的内容模式下有一个标准的 NSTableView,它的 dataSource 是一个附加到我的 AppDelegate 的 managedObjectContext 的数组控制器。在委托中,此代码删除后台线程上的对象(数据库可以有 30k+ 个对象,因此最好有一个响应式 UI、一个进度条和一个取消选项):
-(void)deleteObjects:(NSArray*)objs completionBlock:(void (^)(void))aBlock
{
[self showCancelButton:YES];
__block __typeof__(self) blockself = self;
dispatch_group_t dispatchGroup = dispatch_group_create();
dispatch_group_async(dispatchGroup, dispatch_get_global_queue(0, 0), ^{
NSManagedObjectContext *mainContext = [(AppDelegate*)[[NSApplication sharedApplication] delegate] managedObjectContext];
[objs enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
[mainContext deleteObject:obj];
if ((idx % saveSpace == 0) && (idx > 0)) {
dispatch_sync(dispatch_get_main_queue(), ^{
NSError *error;
[blockself.managedObjectContext save:&error];
theProgressOverlay.filesCount -= saveSpace; //
});
}
if (isCancelled) {
*stop = TRUE;
}
}];
NSError *error;
[mainContext save:&error];
NSLog(@"delete error: %@",error);
if (aBlock) {
dispatch_async(dispatch_get_main_queue(), ^{
[self showCancelButton:NO];
aBlock();
});
}
});
}
此方法由代码调用:
NSFetchRequest *toDelFetch = [NSFetchRequest fetchRequestWithEntityName:entity];
[toDelFetch setIncludesPropertyValues:NO];
NSError *error;
NSArray *toDelObjs = [self.managedObjectContext executeFetchRequest:toDelFetch error:&error];
NSLog(@"Error: %@",error);
if (!toDelObjs || toDelObjs.count < 1) {
return FALSE;
}
[self deleteObjects:toDelObjs completionBlock:{NSLog(@"Finished");}];
一切似乎都很好:用户界面的响应和取消按钮总是有效,但我经常收到错误:
2012-12-13 19:31:53.352 QS[1399:403] *** Assertion failure in -[NSTableRowData _addRowViewForVisibleRow:withPriorView:], /SourceCache/AppKit/AppKit-1138.51/TableView.subproj/NSTableRowData.m:2484
2012-12-13 19:31:53.353 QS[1399:403] Row 1 should be in the valid visible section
2012-12-13 19:31:53.358 QS[1399:403] (
0 CoreFoundation 0x00007fff8b773f56 __exceptionPreprocess + 198
1 libobjc.A.dylib 0x00007fff88715d5e objc_exception_throw + 43
2 CoreFoundation 0x00007fff8b773d8a +[NSException raise:format:arguments:] + 106
3 Foundation 0x00007fff8db1a71f -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:] + 169
4 AppKit 0x00007fff917c6003 -[NSTableRowData _addRowViewForVisibleRow:withPriorView:] + 164
5 AppKit 0x00007fff917c5ef2 -[NSTableRowData _addRowViewForVisibleRow:withPriorRowIndex:inDictionary:withRowAnimation:] + 184
6 AppKit 0x00007fff917c5e38 -[NSTableRowData _addRowViewForVisibleRow:] + 38
7 AppKit 0x00007fff917c557c -[NSTableRowData _unsafeUpdateVisibleRowEntries] + 448
8 AppKit 0x00007fff917c5397 -[NSTableRowData updateVisibleRowViews] + 95
9 AppKit 0x00007fff9175d854 -[NSTableView viewWillDraw] + 156
10 AppKit 0x00007fff916c1f08 -[NSView viewWillDraw] + 666
11 AppKit 0x00007fff916c1f08 -[NSView viewWillDraw] + 666
12 AppKit 0x00007fff916c2796 -[NSScrollView viewWillDraw] + 43
13 AppKit 0x00007fff916c1f08 -[NSView viewWillDraw] + 666
14 AppKit 0x00007fff916c1f08 -[NSView viewWillDraw] + 666
15 AppKit 0x00007fff916c0c4d -[NSView _sendViewWillDrawInRect:clipRootView:suppressRecursion:] + 1358
16 AppKit 0x00007fff916bf9b8 -[NSView displayIfNeeded] + 1039
17 AppKit 0x00007fff916bf375 _handleWindowNeedsDisplayOrLayoutOrUpdateConstraints + 648
18 CoreFoundation 0x00007fff8b7338e7 __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 23
19 CoreFoundation 0x00007fff8b733846 __CFRunLoopDoObservers + 374
20 CoreFoundation 0x00007fff8b708af9 __CFRunLoopRun + 825
21 CoreFoundation 0x00007fff8b708486 CFRunLoopRunSpecific + 230
22 HIToolbox 0x00007fff8cb352bf RunCurrentEventLoopInMode + 277
23 HIToolbox 0x00007fff8cb3c4bf ReceiveNextEventCommon + 181
24 HIToolbox 0x00007fff8cb3c3fa BlockUntilNextEventMatchingListInMode + 62
25 AppKit 0x00007fff91683779 _DPSNextEvent + 659
26 AppKit 0x00007fff9168307d -[NSApplication nextEventMatchingMask:untilDate:inMode:dequeue:] + 135
27 AppKit 0x00007fff9167f9b9 -[NSApplication run] + 470
28 AppKit 0x00007fff918fbeac NSApplicationMain + 867
29 QuickSlide 0x00000001000019c2 main + 34
30 QuickSlide 0x0000000100001994 start + 52
与我们亲爱的朋友谷歌一起搜索,显示了许多博客指出Row 1 should be in the valid visible section
当 NSTableView 被-reloadData
称为“太早”时是一个错误,即在-awakeFromNib
. 但对我来说不是这样——删除肯定是在应用程序完全唤醒之后发生的。我还注释掉了showCancelButton:
以确保它不是我的自定义进度条并尝试了
[contentArrayController setAutomaticallyRearrangesObjects:NO];
谁能告诉我是否有一个狡猾的技巧来避免这种断言?或者即使这很重要?该应用程序似乎可以继续运行,但我看到的其中一个博客说它正在为以后存储麻烦,但没有详细说明。该表在引发错误后似乎确实会更慢地刷新自身。