objective-c - 10.10 中 NSTableView 的 selectRowIndexes 或 scrollRowToVisible 崩溃

标签 objective-c macos cocoa nstableview

在 while 循环中,一旦测试开始,就会重复调用更新 NSTableView 的方法。

......
-(void)runtest:(int)iType wellIndex:(int)nWellIndex
{
    bTestRunning[nWellIndex] = TRUE;

    NSNumber *wellIndex = [NSNumber numberWithInt:nWellIndex];

    [testThread[nWellIndex] release];
    testThread[nWellIndex] = [[NSThread alloc] initWithTarget:self 
                                         selector:@selector(testThreadMainRoutine:) 
                                           object:wellIndex]; 

    [testThread[nWellIndex] start];
}
......

- (void)testThreadMainRoutine:(id)argument
{ 
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 

    int nWellIndex = [(NSNumber *)argument intValue];

    int nStartIndex, nEndIndex;
    if (nWellIndex == 0)
    {
        nStartIndex = 0;
        nEndIndex = ONEWELL_UUT;
    }
    else if(nWellIndex==1)
    {
        nStartIndex = ONEWELL_UUT;
        nEndIndex = TWOWELL_UUT;
    }
    else
    {
        nStartIndex=TWOWELL_UUT;
        nEndIndex=MAX_UUT;
    }

    // Log library version etc.
    if (nWellIndex == 0)
        NSLog(@"*** Left Well Test Start ***");
    else if(nWellIndex==1)
        NSLog(@"*** Middle Well Test Start ***");
    else 
        NSLog(@"*** Right Well Test Start ***");

    // Run
    int i, j, itemCount, bContinue;
    itemCount = 0;
    int lineCount = [cmdArray count];

    for (j=nStartIndex; j<nEndIndex; j++)
    {
        [strPlog[j] release];
        strPlog[j] = [[NSMutableString alloc] init];
    }

    for (i=0; i<lineCount; i++)
    {
        [NSThread sleepForTimeInterval:0.001];

        [self probeForAbortCondition:nWellIndex];

        if ([testThread[nWellIndex] isCancelled])
        {
            bTestStatus[nWellIndex] = ABORT;
            goto TESTTHREAD_EXIT;
        }

        // Is there any UUT available?
        bContinue = 0;
        for (j=nStartIndex; j<nEndIndex; j++)
        {
            if (uutActive[j])
            {
                bContinue = 1;
                break;
            }
        }
        if (bContinue == 0)
        {
            goto TESTTHREAD_EXIT;
        }

        while(bPaused[nWellIndex])
        {
            [NSThread sleepForTimeInterval:0.1];
            [self setTextResult:@"Pause" textColor:[NSColor orangeColor] wellIndex:nWellIndex];
        }
        [self setTextResult:@"Running" textColor:[NSColor orangeColor] wellIndex:nWellIndex];

        // Per Command Line
        CommandLine *cmdLine = [cmdArray objectAtIndex:i];

        // Run UUT independently from this command line
        if ([cmdLine independentRun] == INDEPENDENTRUN_START)
        {
            g_nIndependentStart = i;
            g_nIndependentStop = [self independentRunStopIndex];

            // Dispatch independent thread for each active UUT
            for (j=nStartIndex; j<nEndIndex; j++)
            {
                if (uutActive[j])
                {
                    g_itemCount[j] = itemCount;

                    NSNumber *uutIndex = [NSNumber numberWithInt:j];
                    [g_uutThread[j] release];
                    g_uutThread[j] = [[NSThread alloc] initWithTarget:self 
                        selector:@selector(testThreadUUTRoutine:) 
                                                           object:uutIndex]; 

                    [g_uutThread[j] start]; 
               }
            }

            // Waiting till test abort or UUT thread finish
            while ( 1 )
            {

                [self probeForAbortCondition:nWellIndex];

                // User press button to abort the test
                if ([testThread[nWellIndex] isCancelled])
                {
                    for (j=nStartIndex; j<nEndIndex; j++)
                    {
                        if ([g_uutThread[j] isExecuting])
                        {
                            [g_uutThread[j] cancel];
                        }
                    }
                    bTestStatus[nWellIndex] = ABORT;
                }

                // Wait until all thread exited, then continue remaining test
                bContinue = 0;
                for (j=nStartIndex; j<nEndIndex; j++)
                {
                     if (uutSelected[j])
                    {
                        if ([g_uutThread[j] isExecuting])
                        {
                            bContinue = 1;
                        }
                        else
                        {
                            [g_uutThread[j] release];
                            g_uutThread[j] = nil;
                        }
                    }
                }
                // All UUT thread exited
                if ( ! bContinue)
                {
                    break;
                }

                // Select current test row
                if (itemCount != [self maxCurrentItemCount:nWellIndex])
                {
                    itemCount = [self maxCurrentItemCount:nWellIndex];
                    NSArray *objArray = [NSArray arrayWithObjects:[NSNumber numberWithInt:nWellIndex], [NSNumber numberWithInt:itemCount], nil];
                [self performSelectorOnMainThread:@selector(selectCurrentRow:) withObject:objArray
                                    waitUntilDone:YES];

                }
                if (nWellIndex == 0)
                {
                    [testTable01 display];
                }
                else if(nWellIndex==1)
                {
                    [testTable02 display];
                }
                else
                {
                    [testTable03 display];
                }
           }

            // Test abort or UUT fail and aborted
            if (bTestStatus[nWellIndex] == ABORT)
            {
                goto TESTTHREAD_EXIT;
            }

            if (g_nIndependentStop >= lineCount)
            {
                // All test done
                goto TESTTHREAD_EXIT;
            }
            else
            {
                // Continue remaining test
                i = g_nIndependentStop;
                cmdLine = [cmdArray objectAtIndex:i];
                itemCount = [self maxCurrentItemCount:nWellIndex];
            }
        }

        NSArray *objArray = [NSArray arrayWithObjects:[NSNumber numberWithInt:nWellIndex], [NSNumber numberWithInt:itemCount], nil];
    // Select current test row
        dispatch_async(dispatch_get_main_queue(), ^{
            [self performSelectorOnMainThread:@selector(selectCurrentRow:) withObject:objArray waitUntilDone:YES];
        });


        NSString *cmdName = [cmdLine valueForKey:@"cmdName"];
        NSString *cmdParam = [cmdLine valueForKey:@"cmdParam"];
        NSString *itemName = [cmdLine valueForKey:@"itemName"];

        // Clear returnArray
        for (j=nStartIndex; j<nEndIndex; j++)
        {
            if ([cmdLine flag] & FLAG_SKIP) {

                [[cmdReturn objectAtIndex:j] setUnit:@"NA"];
                [[cmdReturn objectAtIndex:j] setLowerLimit:@"NA"];
                [[cmdReturn objectAtIndex:j] setUpperLimit:@"NA"];

                [[cmdReturn objectAtIndex:j] setCmdResult:@"Skipped"];
                [[cmdReturn objectAtIndex:j] setTestResult:@"Skipped"];
                [[cmdReturn objectAtIndex:j] setCmdStatus:SKIPPED];
                continue;
            }


            [[cmdReturn objectAtIndex:j] setCmdResult:nil];
            [[cmdReturn objectAtIndex:j] setTestResult:nil];
            [[cmdReturn objectAtIndex:j] setCmdStatus:FAIL];

            [[cmdReturn objectAtIndex:j] setUnit:nil];
            [[cmdReturn objectAtIndex:j] setLowerLimit:nil];
            [[cmdReturn objectAtIndex:j] setUpperLimit:nil];

    }




    if (nWellIndex == 0)
        NSLog(@"Left Well Executing %d: %@, (%@), %@", i, cmdName, cmdParam, itemName);
    else if(nWellIndex==1)
        NSLog(@"Middle Well Executing %d: %@, (%@), %@", i, cmdName, cmdParam, itemName);
    else
        NSLog(@"Right Well Executing %d: %@, (%@), %@", i, cmdName, cmdParam, itemName);

    if ([cmdName characterAtIndex:0] == '_') // Per UUT command
    {
        // Repeat for all UUT
        for (j=nStartIndex; j<nEndIndex; j++)
        {
            // whether UUT is selected
            if (uutActive[j])
            {
                if ([cmdLine flag] & FLAG_SKIP) {

                    [[cmdReturn objectAtIndex:j] setUnit:@"NA"];
                    [[cmdReturn objectAtIndex:j] setLowerLimit:@"NA"];
                    [[cmdReturn objectAtIndex:j] setUpperLimit:@"NA"];

                    [[cmdReturn objectAtIndex:j] setCmdResult:@"Skipped"];
                    [[cmdReturn objectAtIndex:j] setTestResult:@"Skipped"];
                    [[cmdReturn objectAtIndex:j] setCmdStatus:SKIPPED];

                    [self saveCmdReading:[[cmdReturn objectAtIndex:j] cmdResult]
                             commandLine:i uutIndex:j lineStatus:SKIPPED];

                    // Process test result
                    if (itemName != nil)
                    {
                        [self saveUutResult:[[cmdReturn objectAtIndex:j] testResult] testItem:itemName uutIndex:j uutStatus:SKIPPED];
                    }

                    continue;
                }


                // Before Item Test, save timestamp


                    if (itemName != nil) {
                        NSString* timeStamp = [[NSDate date] descriptionWithCalendarFormat:@"\n%y-%m-%d %H:%M:%S: "
                                                                                  timeZone:nil locale:nil];
                        [strPlog[j] appendFormat:@"%@ START %@", timeStamp, itemName];
                    }

                    // insert to UART log file
                    char buff[1024];
                    NSString *strInfo = [NSString stringWithFormat:@"%d: %@, (%@), %@", i, cmdName, cmdParam, itemName];
                    sprintf(buff, "\n[Host Executing Cmd]%s\n", [strInfo UTF8String]);
                    [self uartDataLogging:j buffer:buff pureUARTData:NO];




                    [self parseCommand:cmdName parameter:cmdParam lineIndex:i uutIndex:j wellIndex:nWellIndex];

                // Process test result
                    [self saveCmdReading:[[cmdReturn objectAtIndex:j] cmdResult] commandLine:i uutIndex:j lineStatus:[[cmdReturn objectAtIndex:j] cmdStatus]];

                // Test Item
                if (itemName != nil)
                {
                        [self saveUutResult:[[cmdReturn objectAtIndex:j] testResult] testItem:itemName uutIndex:j uutStatus:[[cmdReturn objectAtIndex:j] cmdStatus]];
                    }


                    // After Item Test, save timestamp
                    NSString* timeStamp = [[NSDate date] descriptionWithCalendarFormat:@"\n%y-%m-%d %H:%M:%S: " timeZone:nil locale:nil];
                    [strPlog[j] appendFormat:@"%@ FINISH %@", timeStamp, itemName];
                }

                // Fail Stop/Abort
                if ([[cmdReturn objectAtIndex:j] cmdStatus] == FAIL)
                {
                    if ([cmdLine flag] & FLAG_FAILSTOP)
                    {
                        // Stop this UUT test
                        uutActive[j] = 0;

                        // Close UART
                        [self closeSerialPort:j];

                        // shut down UUT USB & Battery Power
                        uutPower(j, 0);
                    }
                    else if ([cmdLine flag] & FLAG_FAILABORT)
                    {
                        bTestStatus[nWellIndex] = ABORT;
                        goto TESTTHREAD_EXIT;
                    }

                }
            }
        }
    }
    else // For all active UUT
    {
        bool skip = false;

        // Before Item Test, save timestamp
        for (j=nStartIndex; j<nEndIndex; j++)
        {
            if ([cmdLine flag] & FLAG_SKIP) {

                skip = true;

                [[cmdReturn objectAtIndex:j] setUnit:@"NA"];
                [[cmdReturn objectAtIndex:j] setLowerLimit:@"NA"];
                [[cmdReturn objectAtIndex:j] setUpperLimit:@"NA"];

                [[cmdReturn objectAtIndex:j] setCmdResult:@"Skipped"];
                [[cmdReturn objectAtIndex:j] setTestResult:@"Skipped"];
                [[cmdReturn objectAtIndex:j] setCmdStatus:SKIPPED];

                [self saveCmdReading:[[cmdReturn objectAtIndex:j] cmdResult] commandLine:i uutIndex:j lineStatus:SKIPPED];

                // Process test result
                if (itemName != nil)
                {
                    [self saveUutResult:[[cmdReturn objectAtIndex:j] testResult]
                               testItem:itemName uutIndex:j uutStatus:SKIPPED];
                }


                continue;
            }

// whether UUT is selected
            if (uutActive[j])
            {
                if (itemName != nil)
                {
                    NSString* timeStamp = [[NSDate date] descriptionWithCalendarFormat:@"\n%y-%m-%d %H:%M:%S: " timeZone:nil locale:nil];
                    [strPlog[j] appendFormat:@"%@ START %@", timeStamp, itemName];
                }

                // insert to UART log file
                char buff[1024];
                NSString *strInfo = [NSString stringWithFormat:@"%d: %@, (%@), %@", i, cmdName, cmdParam, itemName];
                sprintf(buff, "\n[Host Executing Cmd]%s\n", [strInfo UTF8String]);
                [self uartDataLogging:j buffer:buff pureUARTData:NO];
            }
        }

        if (skip) {
            continue;
        }


        [self parseCommand:cmdName parameter:cmdParam lineIndex:i uutIndex:-1 wellIndex:nWellIndex];

        // Process command return result
        for (j=nStartIndex; j<nEndIndex; j++)
        {
            // whether UUT is selected
            if (uutActive[j])
            {
                [self saveCmdReading:[[cmdReturn objectAtIndex:j] cmdResult] commandLine:i uutIndex:j lineStatus:[[cmdReturn objectAtIndex:j] cmdStatus]];

                // Process test result
                if (itemName != nil)
                {
                    [self saveUutResult:[[cmdReturn objectAtIndex:j] testResult] testItem:itemName uutIndex:j uutStatus:[[cmdReturn objectAtIndex:j] cmdStatus]];

                    // After Item Test, save timestamp
                    NSString* timeStamp = [[NSDate date] descriptionWithCalendarFormat:@"\n%y-%m-%d %H:%M:%S: " timeZone:nil locale:nil];
                    [strPlog[j] appendFormat:@"%@ FINISH %@", timeStamp, itemName];
                }

                // Fail Stop/Abort
                if ([[cmdReturn objectAtIndex:j] cmdStatus] == FAIL)
                {
                    if ([cmdLine flag] & FLAG_FAILSTOP)
                    {
                        // Stop this UUT test
                        uutActive[j] = 0;

                        // Close UART
                        [self closeSerialPort:j];

                        // shut down UUT USB & Battery Power
                        uutPower(j, 0);

                    }
                    else if ([cmdLine flag] & FLAG_FAILABORT)
                    {
                        bTestStatus[nWellIndex] = ABORT;
                        goto TESTTHREAD_EXIT;
                    }
                    else if ([cmdLine flag] & FLAG_SKIP)
                    {
                        [[cmdReturn objectAtIndex:j] setCmdStatus:SKIPPED];
                        [self saveUutResult:[[cmdReturn objectAtIndex:j] testResult] testItem:itemName uutIndex:j uutStatus:[[cmdReturn objectAtIndex:j] cmdStatus]];
                    }

                }
            }
        }
    }

    if (itemName != nil)
        itemCount++;
}

TESTTHREAD_EXIT:

// Turn Off USB & Battery Power
for (j=nStartIndex; j<nEndIndex; j++)
{
    strTestTimeRecord[j] = [[NSString stringWithString:strPlog[j]] retain];
    [strPlog[j] release];
    strPlog[j] = nil;

    // whether UUT is selected
    if (uutActive[j])
    {
        // Close UART
        [self closeSerialPort:j];

        // shut down UUT USB & Battery Power
        uutPower(j, 0);

    }

    //Clear Buffer for Smokey
    [self clearRdBuffStr:j];
 }


// Refresh UI to indicate test result
[self performSelectorOnMainThread:@selector(afterRun:) withObject:argument waitUntilDone:NO];

[pool release]; 

}

......
-(void)selectCurrentRow:(NSArray*)objArray
{
    int nWellIndex = [[objArray objectAtIndex:0] intValue];
    int itemCount = [[objArray objectAtIndex:1] intValue];

    if (nWellIndex == 0)
    {
        [testTable01 selectRowIndexes:[NSIndexSet indexSetWithIndex:itemCount] byExtendingSelection:NO];
        [testTable01 scrollRowToVisible:itemCount];
    }
    else if(nWellIndex == 1)
    {
        [testTable02 selectRowIndexes:[NSIndexSet indexSetWithIndex:itemCount] byExtendingSelection:NO];
        [testTable02 scrollRowToVisible:itemCount];
    }
    else
    {
        [testTable03 selectRowIndexes:[NSIndexSet indexSetWithIndex:itemCount] byExtendingSelection:NO];
        [testTable03 scrollRowToVisible:itemCount];
    }
}

我的应用程序经常崩溃,崩溃日志如下。我不确定为什么,也无法弄清楚崩溃消息的含义。谁能给我指点一下吗?谢谢。

......
Time Awake Since Boot: 130 seconds

Crashed Thread:        4

Exception Type:        EXC_BAD_ACCESS (SIGSEGV)
Exception Codes:       KERN_INVALID_ADDRESS at 0x00006d1920bdbec0

VM Regions Near 0x6d1920bdbec0:
MALLOC_NANO            0000608000400000-0000608001c00000 [ 24.0M] rw-/rwx   SM=PRV  
--> 
MALLOC_TINY            00007fb048c00000-00007fb049000000 [ 4096K] rw-/rwx SM=PRV  

Application Specific Information:
objc_msgSend() selector name: indexGreaterThanIndex:
......
Thread 4 Crashed:
0   libobjc.A.dylib                 0x00007fff8640e0dd objc_msgSend + 29
1   com.apple.AppKit                0x00007fff88f44c6a -[NSTableView highlightSelectionInClipRect:] + 728
2   com.apple.AppKit                0x00007fff88f43e43 -[NSTableView drawRect:] + 1439
3   com.apple.AppKit                0x00007fff88db5799 -[NSView _drawRect:clip:] + 4238
4   com.apple.AppKit                0x00007fff88db3e0a -[NSView _recursiveDisplayAllDirtyWithLockFocus:visRect:] + 1875
5   com.apple.AppKit                0x00007fff88db420e -[NSView _recursiveDisplayAllDirtyWithLockFocus:visRect:] + 2903
6   com.apple.AppKit                0x00007fff88db420e -[NSView _recursiveDisplayAllDirtyWithLockFocus:visRect:] + 2903
7   com.apple.AppKit                0x00007fff88db420e -[NSView _recursiveDisplayAllDirtyWithLockFocus:visRect:] + 2903
8   com.apple.AppKit                0x00007fff88db420e -[NSView _recursiveDisplayAllDirtyWithLockFocus:visRect:] + 2903
9   com.apple.AppKit                0x00007fff88db420e -[NSView _recursiveDisplayAllDirtyWithLockFocus:visRect:] + 2903
10  com.apple.AppKit                0x00007fff88db420e -[NSView _recursiveDisplayAllDirtyWithLockFocus:visRect:] + 2903
11  com.apple.AppKit                0x00007fff88db1ca6 -[NSView _recursiveDisplayRectIfNeededIgnoringOpacity:isVisibleRect:rectIsVisibleRectForView:topView:] + 913
12  com.apple.AppKit                0x00007fff88db1403 -[NSThemeFrame _recursiveDisplayRectIfNeededIgnoringOpacity:isVisibleRect:rectIsVisibleRectForView:topView:] + 333
13  com.apple.AppKit                0x00007fff88dad79b -[NSView _displayRectIgnoringOpacity:isVisibleRect:rectIsVisibleRectForView:] + 2761
14  com.apple.AppKit                0x00007fff88d652be -[NSView displayIfNeeded] + 1876
15  com.TopTestDFU                  0x000000010b966d31 -[AppController(TestEngine) testThreadMainRoutine:] + 1825
16  com.apple.Foundation            0x00007fff8f26fdc2 __NSThread__main__ + 1345
17  libsystem_pthread.dylib         0x00007fff89880268 _pthread_body + 131
18  libsystem_pthread.dylib         0x00007fff898801e5 _pthread_start + 176
19  libsystem_pthread.dylib         0x00007fff8987e41d thread_start + 13

最佳答案

要获得更具体的答案,您需要提供包含您提到的 while 循环的代码。

EXC_BAD_ACCESS 是一个内存管理错误。具体来说,消息被发送到地址不再有效(已被释放)的对象。我怀疑这是一个计时问题,因为您在修改其数据源集合时反复请求刷新主线程上的表选择。有时它正在访问有效的东西;有时它正在访问一些有效的东西。其他时候则不然。

有两件事让我担心:1)在 while 循环中排队一百万个与 UI 相关的更新(而不是用计时器等合理地限制 UI 更新),2)奇怪地使用集合的计数作为行索引(如果 count 为 0,则选择第 0 行,意味着没有第 0 行?如果 count 为 3,则选择第 0-3 行,意味着只有 0-2 行是有效行?)。

尝试在更新开始时启动半秒或一秒重复计时器,请求 TableView 自行更新,然后在每次触发时调整其选择(当然是在主队列上)。在同一操作中同时执行这两项操作可确保表反射(reflect)数据,从而使选择有意义。然后在所有更新完成后停止/关闭/取消计时器(如果发生过这种情况;如果是“始终更新”,则一定要选择更长的刷新间隔,因为多秒长的更新不需要如此频繁的刷新)。

至于索引范围,这很容易调整 - 全选应该是一个简单的范围NSMakeRange(0, collection.count)(从第一项开始,长度为计数)。不选择任何内容都应该是空集[NSIndexSet indexSet]

更新

此外,在您的 dispatch_async 调用中,您已经指定了主队列,因此调用 -performSelectorOnMainThread:... 是不必要且浪费的。实际上,您是在说“安排告诉主队列安排告诉主队列执行以下操作...”

关于objective-c - 10.10 中 NSTableView 的 selectRowIndexes 或 scrollRowToVisible 崩溃,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29793861/

相关文章:

java - 如何在 Mac OS 10.8.4 上切换 java 版本

objective-c - 优胜美地 : NSSavePanel: __NSArrayI objectAtIndex:]: index 0 beyond bounds for empty array

iphone - KVO 和 NSNotifications 的性能速度?

iphone - NSString的initWithData :encoding: return type issue

objective-c - 如何让自定义对象显示在 Interface Builder 对象面板中?

ios - 检查 Objective-C 字符串中的特定字符

macos - 如何根据拖动的控件动态滚动NSScrollView

ios - 如何在 Xcode 6 或更高版本中创建类别?

ios - 首次使用应用程序的徒步旅行

objective-c - Retina "@2x"图形在标准清晰度显示器上错误使用(使用 Snow Leopard/Xcode 4.2 )