0

我在基于单元格的内容模式下有一个标准的 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];

谁能告诉我是否有一个狡猾的技巧来避免这种断言?或者即使这很重要?该应用程序似乎可以继续运行,但我看到的其中一个博客说它正在为以后存储麻烦,但没有详细说明。该表在引发错误后似乎确实会更慢地刷新自身。

4

1 回答 1

0

我想我已经想通了。大约一周后,我没有遇到问题...... S * d's Law意味着现在我已经回答了我自己的问题,它可能会再次开始。

无论如何,我使用的是委托方法:

- (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex

- (NSInteger)numberOfRowsInTableView:(NSTableView *)aTableView

该表似乎通过我的 CD DB 中的 NSManagedObjects 缓存的 NSArray 来填充我想要的。

我主要是在删除对象时遇到错误(尽管我认为,使用 NSFetchRequest 更新数组缓存)。

因此,以一种抓住稻草的方式,我决定在每个表列中对 NSTextFieldCell 进行子类化,并将 NSTableColumns 绑定到我的主 nib 文件中的数组控制器。这出人意料地好,并且在将鼠标悬停在表格行上时增加了关于工具提示的好处。无论如何,触摸的木头比正确的说法要多,现在一切似乎都平静下来了......如果情况随时发生变化,我会更新这个答案。

更新:似乎 NSTableView 和/或多线程不适合 UI api。我发现从后台线程返回时,诸如 NSDrawers 和工作表之类的东西不会动画——它们会忽略它们的标准方法,如 -orderOut 和 -toggle。使用上面的内容和带有延迟动画的 NSView(延迟后执行选择器)似乎已经完全解决了应用程序。

于 2012-12-21T12:17:37.620 回答