我已经阅读了几个处理类似问题的线程,但我无法弄清楚我过度释放的内容。从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的一个实例。
答案 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中调用。