еще одна странная проблема с 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;
}