第二次保存NSManagedObjectContext时出错(取消分配的insance)

时间:2011-04-09 15:56:22

标签: iphone objective-c core-data nsfetchedresultscontroller nsmanagedobjectcontext

我已经阅读了几个处理类似问题的线程,但我无法弄清楚我过度释放的内容。从Player对象的详细视图控制器,我推动UITableViewController为该Player选择Location对象:

- (void)selectLocations 
{
    LocationSelectionController *vc = [[LocationSelectionController alloc] initWithStyle:UITableViewStyleGrouped];
    vc.player = player;
    [self.navigationController pushViewController:vc animated:YES];
    [vc release];
}

以下是LocationSelectionController的一些细节:

- (void)saveContext {
    NSManagedObjectContext *context = [player managedObjectContext];
    if ([context hasChanges]) {
    NSError *error = nil;
        if (![context save:&error]) { //*** ERROR HERE ***
           // show alert
        }
    }
}

- (void)viewDidLoad {
    [super viewDidLoad];
    // ....

    NSError *error = nil;
    if (![[self fetchedResultsController] performFetch:&error]) {
        // show alert
    }
}

- (NSFetchedResultsController *)fetchedResultsController {
    if (_fetchedResultsController != nil) {
        return _fetchedResultsController;
    }
    NSManagedObjectContext *context = [player managedObjectContext];
    NSFetchRequest *request = [[NSFetchRequest alloc] init];
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"Location" inManagedObjectContext:context];
    [request setEntity:entity];

    NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"name" ascending:YES selector:@selector(caseInsensitiveCompare:)];
    NSArray *sortDescriptors = [NSArray arrayWithObjects:sortDescriptor, nil];
    [request setSortDescriptors:sortDescriptors];

    NSFetchedResultsController *aFRC = [[NSFetchedResultsController alloc] initWithFetchRequest:request managedObjectContext:context sectionNameKeyPath:nil cacheName:nil];
    aFRC.delegate = self;
    self.fetchedResultsController = aFRC;

    [request release];
    [sortDescriptor release];

    return _fetchedResultsController;
}

- (void)viewDidUnload {
    [super viewDidUnload];
    self.fetchedResultsController = nil;
}


- (void)dealloc {
    self.fetchedResultsController = nil;
    [_fetchedResultsController release];
    [player release];
    [super dealloc];
}

第一次导航到LocationSelectionController时,功能总是很完美。我可以毫无问题地与Location对象进行交互。当我弹出视图并返回到Player对象的详细视图时,再次有完美的功能。只有当我第二次推送LocationSelectionController时(即使它来自不同的Player对象),才会发生崩溃,试图保存上下文。

*** -[LocationSelectionController controllerWillChangeContent:]: message sent to deallocated instance 0x7026920

我尝试使用NSZombie的工具来查找问题,它指向了LocationSelectionController的一个实例。 Instruments Screen

2 个答案:

答案 0 :(得分:1)

你对NSFetchedResultsController的处理搞砸了。首先,请考虑以下代码:

NSFetchedResultsController *aFRC = [[NSFetchedResultsController alloc] initWithFetchRequest:request managedObjectContext:context sectionNameKeyPath:nil cacheName:nil];
aFRC.delegate = self;
self.fetchedResultsController = aFRC;

如果您的fetchedResultsController属性被声明为assign,则此代码或多或少是正确的(尽管语义错误)。如果声明属性为retain,则表示您在此处泄露了对aFRC的引用。

但无论如何,你的解除分配是错误的。在dealloc中,你有这个:

self.fetchedResultsController = nil;
[_fetchedResultsController release];

第一行将属性(可能是_fetchedResultsController ivar)设置为nil,因此第二行永远不会释放该对象。如果属性被声明为retain,我想这是为了清理上面提到的泄漏;如果它被声明assign,这就是上面提到的错误语义的证据。即使这样做“正确”,viewDidUnload中的版本也没有额外的版本,所以仍然错误。

无论哪种方式,泄漏的引用都会使NSFetchedResultsController保持活动状态。最终它会找到一个更改,它会尝试发送给它的委托。当然,这也失败了,因为该代表早已被解除分配。


您可能永远不需要从此类的实现外部分配fetchedResultsController。你真正应该做的是这样的事情:

属性fetchedResultsController应声明为:

@property (retain, readonly) NSFetchedResultsController *fetchedResultsController;

您现有的fetchedResultsController没问题,但应该直接指定_fetchedResultsController ivar,而不是尝试分配self.fetchedResultsController

然后dealloc和viewDidUnload方法应该各自释放这样的对象:

[_fetchedResultsController release];
_fetchedResultsController = nil;

你可以在发布它之前设置_fetchedResultsController.delegate nil,这是非常确定的,但文档并没有表明这是必要的,因为它对其他一些类也是如此。

答案 1 :(得分:0)

我有一个类似的问题,结果不同。

就我而言,我在控制器中有一些KVO观察者没有相应的removeObserver:forKeyPath:在dealloc中调用。