Ich habe eine Kerndaten App mit 2 Ansichten. Die erste Ansicht listet "Räume" auf, die zweite Liste "Szenen" in Räumen. Die Rooms-Seite verfügt über eine NavItem-Schaltfläche zum Bearbeiten, die, wenn sie gedrückt wird, eine NavItem-Schaltfläche hinzufügt. Sie können Räume von hier löschen und hinzufügen. Hinzugefügte Räume erscheinen einfach mit einem Standardnamen "Neuer Raum" in der Tabelle. Die zweite Ansicht ist eine Liste von Szenen im ausgewählten Raum. Gleiches gilt hier, Sie können Szenen löschen und hinzufügen, und hinzugefügte Szenen erscheinen einfach in der Tabelle mit dem Namen "Neue Szene". Nichts Besonderes wirklich.Core Data App stürzt ab mit "controllerWillChangeContent: unerkannter Selektor an Instanz gesendet"
Ich verwende eine FetchedResultsController
in beiden View-Controller, mit den Szenen eine NSPredicate
, nur Szenen aus dem ausgewählten Raum zurückgeben. Ich verwende auch die controllerWillChangeContent
, controllerDidChangeContent
etc. Delegate-Methoden für die Tabellenansicht Updates.
Dies alles funktioniert zuerst gut, aber in der Regel nach dem Navigieren in Räumen und Szenen und dann versuchen, eine Szene zu löschen, wird es abstürzen. Es wird unweigerlich zum Absturz bringen, wenn Sie lange genug damit spielen. Es passiert nur beim Löschen einer Szene. Wenn Sie die Bearbeitungsschaltfläche drücken und eine Szene löschen und es funktioniert, funktionieren alle folgenden Löschungen in dieser Bearbeitungssitzung immer. Es stürzt nur beim ersten Löschen der Editiersitzung ab.
Der Fehler, den ich bekommen, ist ein seltsamer:
App beenden aufgrund nicht abgefangene Ausnahme 'NSInvalidArgumentException', Grund: '- [__ NSCFType controllerWillChangeContent:]: Unbekannter Selektor gesendet Instanz 0x5e02d70'
Der erste Teil dieses Fehlers ändert sich manchmal. Manchmal ist es __NSCFType
, manchmal ist es CALayer
. Dieser Fehler tritt nur beim Löschen von Szenen auf. Hinzufügen von Szenen ist in Ordnung 100%.
Ich habe einen anderen Beitrag auf Stackoverflow gelesen, die darauf hindeutet, dass diese Art von Fehlern von Speicherverwaltungsproblemen kommen kann. Ich habe den Code zweimal überprüft und mit dem Leckinstrument durch die Instrumente geleitet. Es gibt keine Lecks.
Kann ich noch etwas überprüfen? Irgendwelche Ideen?
Hier ist der entsprechende Code ..
Von ScenesTableViewController.m:
// used to show/hide the add button
- (void)setEditing:(BOOL)editing animated:(BOOL)animate
{
[super setEditing:editing animated:animate];
if(editing)
{
self.navigationItem.leftBarButtonItem = addButton;
}
else
{
self.navigationItem.leftBarButtonItem = nil;
}
}
// called when the add button is pressed
- (void)addAction {
NSEntityDescription *myContentEntity = [NSEntityDescription entityForName:@"Scene" inManagedObjectContext:managedObjectContext];
Scene *contentToSave = [[Scene alloc] initWithEntity:myContentEntity insertIntoManagedObjectContext:managedObjectContext];
[contentToSave setValue:@"New Scene" forKey:@"Name"];
[parentRoom addRoomToScenesObject:contentToSave];
NSError *error;
if (![managedObjectContext save:&error]) {
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
exit(-1);
}
[contentToSave release];
}
// delegate method that's being sent unrecognised selectors
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
// The fetch controller is about to start sending change notifications, so prepare the table view for updates.
[self.tableView beginUpdates];
}
// cell display code
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = nil;
cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (!cell) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle
reuseIdentifier:CellIdentifier] autorelease];
[cell setAccessoryType:UITableViewCellAccessoryDisclosureIndicator];
}
[self configureCell:cell atIndexPath:indexPath];
return cell;
}
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath {
NSManagedObject *mo = nil;
NSString *temp = nil;
mo = [fetchedResultsController objectAtIndexPath:indexPath];
temp = [mo valueForKey:@"Name"];
[[cell textLabel] setText:temp];
}
// cell editing code
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
// Delete the managed object at the given index path.
[managedObjectContext deleteObject:[fetchedResultsController objectAtIndexPath:indexPath]];
NSError *error;
if (![managedObjectContext save:&error]) {
// Update to handle the error appropriately.
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
exit(-1); // Fail
}
}
else if (editingStyle == UITableViewCellEditingStyleInsert) {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
}
}
// NSFetchedResultsController code
- (NSFetchedResultsController *)fetchedResultsController {
if (fetchedResultsController != nil) {
return fetchedResultsController;
}
/*
Set up the fetched results controller.
*/
// Create the fetch request for the entity.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
// Edit the entity name as appropriate.
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Scene" inManagedObjectContext:managedObjectContext];
NSSortDescriptor *nameDescriptor = [[NSSortDescriptor alloc] initWithKey:@"Name" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:nameDescriptor, nil];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"(SceneToRoom == %@)", parentRoom];
[fetchRequest setSortDescriptors:sortDescriptors];
[fetchRequest setPredicate:predicate];
[fetchRequest setEntity:entity];
// Edit the section name key path and cache name if appropriate.
// nil for section name key path means "no sections".
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:managedObjectContext sectionNameKeyPath:nil cacheName:nil];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
[aFetchedResultsController release];
[fetchRequest release];
[nameDescriptor release];
[sortDescriptors release];
return fetchedResultsController;
}
Danke für die Antwort Marcus. Meine NSFetchedResultsControllers sind synthetisierte Eigenschaften ihrer UITableViewControllers. In der ViewDidUnload-Methode habe ich self.fetchedResultsController = nil, um den Besitz aufzugeben. Muss ich geholtResultsController auch in dealloc freigeben? –
Ja, jede Eigenschaft, die als 'retain' * festgelegt wurde, muss im' -dealloc' freigegeben werden. Tatsächlich würde ich ** sehr ** empfehlen, alle Eigenschaften auf "Null" zu setzen, auch wenn sie in beiden Methoden nur "zuweisen" sind. –
Vielen Dank Marcus, das ist behoben. Und danke für den Tipp zu den behaltenen Eigenschaften und dem -dealloc. Prost! –