UITableView падает после попытки удалить ячейку * второй * раз - PullRequest
1 голос
/ 21 сентября 2009

еще одна странная проблема с iPhone SDK здесь.

У меня есть UITableView, который содержит пользовательский UITableViewCell. Эта таблица получает данные из изменяемого массива, который, в свою очередь, извлекает их из хранилища Core Data.

Проблема: Если я вхожу в режим редактирования и удаляю ячейку, она работает впервые - ячейка успешно удаляется из UITableView и базы данных Core Data. Однако, если я пытаюсь удалить другую ячейку, приложение вылетает после нажатия кнопки «Удалить» для строки (той, которая фактически запускает действие, а не кнопки, которая вращается, чтобы показать своего партнера).

Это мой код метода делегата 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 Ответ

4 голосов
/ 21 сентября 2009

Вероятно, что NSInternalInconsistencyException поднимается (проверьте вашу консоль).

Вам необходимо убедиться, что при вызове tableView:numberOfRowsInSection: на вашем UITableViewDelegate счетчик верен для той части ячейки, которая была удалена с помощью deleteRowsAtIndexPaths:withRowAnimation:. Т.е. если в секции 0 было 10 элементов, и вы удалили один из них с помощью deleteRowsAtIndexPaths:withRowAnimation:, tableView:numberOfRowsInSection: должен вернуть 9 для секции 0.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...