尝试删除* second * time后的单元格后,UITableView崩溃

时间:2009-09-20 22:20:14

标签: iphone objective-c cocoa-touch uitableview

iPhone SDK的另一个奇怪问题。

我有UITableView,其中包含自定义UITableViewCell。该表从可变数组中获取数据,该数组又从Core Data存储中获取数据。

问题:如果我进入编辑模式并删除一个单元格,它将首次运行 - 该单元格已成功从UITableView和Core Data数据库中删除。但是,如果我尝试删除另一个单元格,则在单击行的“删除”按钮(实际触发操作的按钮,而不是旋转自身以显示其伙伴的按钮)后应用程序崩溃。 / p>

这是我的代码,用于删除行的UITableView委托方法:

- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
    if ( editingStyle == UITableViewCellEditingStyleDelete ) {
        NSManagedObject *dream = [dreamsArray objectAtIndex:indexPath.row]; 
        [managedObjectContext deleteObject:dream];

        [dreamsArray removeObjectAtIndex:indexPath.row];
        // removing from instance field array works fine, but...
        [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
        // ... crash right here

        NSError *error = nil;
        if ( ![[self managedObjectContext] save:&error] )
            NSLog(@"DreamsViewController:140 %@", error);
    }
}

我追查了有问题的方法,发现deleteRowsAtIndexPaths:withRowAnimation:(令人惊讶)是罪魁祸首。我已经尝试退出编辑模式,访问另一个视图,并重新访问此表以删除另一个视图,但这也不起作用。


更新:我添加了一项测试,以确保在删除元素后不会向UITableView提供错误的行值,从而导致其崩溃。修改后的代码:

- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
    if ( editingStyle == UITableViewCellEditingStyleDelete ) {
        NSManagedObject *dream = [dreamsArray objectAtIndex:indexPath.row];
        [managedObjectContext deleteObject:dream];

        // outputs right before a cell is deleted
        NSLog(@"before\tarray\t%d", [dreamsArray count]);

        [dreamsArray removeObjectAtIndex:indexPath.row];
        [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];

        NSError *error = nil;
        if ( ![[self managedObjectContext] save:&error] )
            NSLog(@"DreamsViewController:129 %@", error);
    }
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    // outputs after a cell is deleted
    NSLog(@"after\tarray\t%d", [dreamsArray count]);
    return [dreamsArray count];
}

调试控制台在删除单元格时显示没有任何逻辑错误(并删除了相应的对象):

** pressed "Delete" for the first time **
2009-09-21 16:47:12.567 xxx[774:20b] before array   11
2009-09-21 16:47:12.568 xxx[774:20b] after  array   10

** pressed "Delete" on another cell after first deletion finished **
2009-09-21 16:47:17.685 xxx[774:20b] before array   10
2009-09-21 16:47:17.686 xxx[774:20b] after  array   9
** after this output, CRASH occurs **

更新:这是堆栈跟踪:

#0  0x94292688 in objc_msgSend ()
#1  0x016c867d in -[UITableView dequeueReusableCellWithIdentifier:] ()
#2  0x000038c4 in -[DreamsViewController tableView:cellForRowAtIndexPath:] (self=0x3e1e380, _cmd=0x29cfb18, tableView=0x401b800, indexPath=0x3e4ece0) at /Users/hans/Projects/xxx/Classes/DreamsViewController.m:85
#3  0x016cee60 in -[UITableView(UITableViewInternal) _createPreparedCellForGlobalRow:withIndexPath:] ()
#4  0x016c6a97 in -[UITableView(UITableViewInternal) _createPreparedCellForGlobalRow:] ()
#5  0x01866a18 in -[_UITableViewUpdateSupport(Private) _setupAnimationsForExistingOffscreenCells] ()
#6  0x01869a28 in -[_UITableViewUpdateSupport initWithTableView:updateItems:oldRowData:newRowData:oldRowRange:newRowRange:context:] ()
#7  0x016d6a2b in -[UITableView(_UITableViewPrivate) _updateWithItems:withOldRowData:oldRowRange:newRowRange:context:] ()
#8  0x016d6757 in -[UITableView(_UITableViewPrivate) _endCellAnimationsWithContext:] ()
#9  0x016c8a77 in -[UITableView deleteRowsAtIndexPaths:withRowAnimation:] ()
#10 0x00003ec3 in -[DreamsViewController tableView:commitEditingStyle:forRowAtIndexPath:] (self=0x3e1e380, _cmd=0x29cfaa0, tableView=0x401b800, editingStyle=UITableViewCellEditingStyleDelete, indexPath=0x3e4e380) at /Users/hans/Projects/xxx/Classes/DreamsViewController.m:125
#11 0x016c66fc in -[UITableView(UITableViewInternal) animateDeletionOfRowWithCell:] ()
#12 0x0167f459 in -[UIApplication sendAction:to:from:forEvent:] ()
#13 0x016e2ba2 in -[UIControl sendAction:to:forEvent:] ()
#14 0x016e4dc3 in -[UIControl(Internal) _sendActionsForEvents:withEvent:] ()
#15 0x016e3b0f in -[UIControl touchesEnded:withEvent:] ()
#16 0x01698e33 in -[UIWindow _sendTouchesForEvent:] ()
#17 0x0168281c in -[UIApplication sendEvent:] ()
#18 0x016890b5 in _UIApplicationHandleEvent ()
#19 0x0001def1 in PurpleEventCallback ()
#20 0x00843b80 in CFRunLoopRunSpecific ()
#21 0x00842c48 in CFRunLoopRunInMode ()
#22 0x0001c7ad in GSEventRunModal ()
#23 0x0001c872 in GSEventRun ()
#24 0x0168a003 in UIApplicationMain ()
#25 0x00005461 in main (argc=1, argv=0xbfffef54) at /Users/hans/Projects/xxx/main.m:14

因此,在查看堆栈跟踪之后,看起来dequeueReusableCellWithIdentifier:也与此问题有关。这是调用该方法的代码,虽然我没有看到任何错误,但是......

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    static NSString *cellID = @"dreamCell";

    DreamTableCell *cell = (DreamTableCell *)[tableView dequeueReusableCellWithIdentifier:cellID];
    if ( cell == nil )
        cell = [[[DreamTableCell alloc] initWithFrame:CGRectZero reuseIdentifier:cellID] autorelease];

    Dream *dream = [dreamsArray objectAtIndex:indexPath.row];
    [cell dreamLabel].text = [dream dreamContent];

    [dateFormatter setDateFormat:@"dd"];
    cell.dayLabel.text = [dateFormatter stringFromDate:[dream date]];

    [dateFormatter setDateFormat:@"HH:mm"];
    cell.timeLabel.text = [dateFormatter stringFromDate:[dream date]];

    return cell;
}

1 个答案:

答案 0 :(得分:4)

可能会引发NSInternalInconsistencyException(检查您的控制台)。

您需要确保在tableView:numberOfRowsInSection:上调用UITableViewDelegate时,对于通过deleteRowsAtIndexPaths:withRowAnimation:删除的单元格部分的计数是正确的。即如果第0部分中有10个元素,并且您通过deleteRowsAtIndexPaths:withRowAnimation:删除了其中一个元素,则tableView:numberOfRowsInSection: 必须为第0部分返回9。