核心数据应用程序崩溃,显示"controllerWillChangeContent: unrecognized selector sent to instance"。

7
我有一个核心数据应用程序,其中包含2个视图。第一个视图列出“房间”,第二个视图列出房间中的“场景”。房间页面有一个编辑 NavItem 按钮,按下该按钮后会启用添加 NavItem 按钮。您可以在此处删除和添加房间。添加的房间仅以默认的“新房间”名称显示在表格中。第二个视图是所选房间中场景的列表。在这里同样可以删除和添加场景,添加的场景只会以名称“新场景”显示在表格中。真没什么特别的。
我在两个视图控制器中都使用了 FetchedResultsController,其中 Scenes 一个具有 NSPredicate,以仅返回所选房间的场景。我还使用了 controllerWillChangeContent、controllerDidChangeContent 等委托方法来更新表视图。
这一切起初都很正常,但通常在浏览房间和场景之后尝试删除场景时会崩溃。如果你玩得够久,它肯定会崩溃。它只在删除场景时发生。如果按下编辑按钮并删除场景,那么该编辑会话中的所有后续删除都将始终有效。它只会在编辑会话的第一次删除时崩溃。
我收到的错误信息很奇怪:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFType controllerWillChangeContent:]: unrecognized selector sent to instance 0x5e02d70'
这个错误的第一部分有时会改变。有时是 __NSCFType,有时是 CALayer。这个错误只发生在删除场景时。添加场景完全没有问题。
我读过 stackoverflow 上的另一篇文章,该文章建议这些错误可能来自内存管理问题。我已经仔细检查了代码,并通过泄漏工具将其运行。没有泄漏。
还有其他我可以检查的吗?有什么想法吗?
以下是相关代码...
来自 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;
} 
2个回答

28

这个错误很可能是来自一个已被释放的代理 NSFetchedResultsController。你是否有一个已释放但没释放相关联的 NSFetchedResultsControllerUIViewController


感谢您的回复,马库斯。我的NSFetchedResultsControllers是它们的UITableViewControllers的合成属性。在viewDidUnload方法中,我使用self.fetchedResultsController = nil来放弃所有权。我还需要在dealloc中释放fetchedResultsController吗? - Max Clarke
是的,任何被设置为retain的属性必须-dealloc中释放。实际上,我强烈建议在这两个方法中将所有属性设置为nil,即使它们只是assign - Marcus S. Zarra
非常感谢你,马库斯,问题已经解决了。还要感谢你关于保留属性和-dealloc的提示。干杯! - Max Clarke
5
viewDidUnload很遗憾在dealloc中不会被调用。另外,为了防止其他地方还持有对它的引用,最好做一些类似于self.fetchedResultsController.delegate = nil的操作。 - tc.
你能帮我吗?我遇到了几乎相同的问题。我已经发布了获取结果控制器,但有时仍会抛出以下错误:NSCFString controllerWillChangeContent || NSSQLRow controllerWillChangeContent || [UILabel persistentStoreCoordinator]。如果你能帮我解决这个问题,我将不胜感激。这是我的问题链接:http://stackoverflow.com/questions/5179071/how-to-solve-nsinvalidargumentexception。 - Csabi
Marcus的笔记也为我解决了这个问题 - 总是要记得在释放之前将委托设为nil! - Jason

1

我也遇到了同样的问题。但是对于iOS 4.2,问题在于NSError被初始化了,因此它被视为垃圾,并且在更新/插入时我们会出现

if (![managedObjectContext save:&error]) {
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
        exit(-1);
    }

因此,在保存时,iOS将错误视为垃圾并将异常视为垃圾。尝试将其初始化为nil。这解决了我的问题。该问题仅在4.2iOS中出现。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接