第二次保存 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 时,该功能总是很完美。我可以毫无问题地与位置对象进行交互。当我弹出视图并返回到 Player 对象的详细视图时,再次出现完美的功能。仅当我第二次推送 LocationSelectionController 时(即使它来自不同的 Player 对象),尝试保存上下文时才会发生崩溃。
*** -[LocationSelectionController controllerWillChangeContent:]: message sent to deallocated instance 0x7026920
我尝试使用 NSZombie 的仪器来查找问题,它向我指出了 LocationSelectionController 的实例。
I've read several threads dealing with similar issues on here, but I just can't figure out what I am over-releasing. From a detail view controller for a Player object, I push a UITableViewController to select Location objects for that Player:
- (void)selectLocations
{
LocationSelectionController *vc = [[LocationSelectionController alloc] initWithStyle:UITableViewStyleGrouped];
vc.player = player;
[self.navigationController pushViewController:vc animated:YES];
[vc release];
}
Here is a look at some details of the 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];
}
The functionality is always perfect the first time I navigate to the LocationSelectionController. I can interact with the Location objects with no problems at all. When I pop the view and return to the detail view of the Player object, again there is perfect functionality. It is only when I push the LocationSelectionController for a second time (even if it is from a different Player object) that there is a crash up attempting to save the context.
*** -[LocationSelectionController controllerWillChangeContent:]: message sent to deallocated instance 0x7026920
I've tried using instruments with NSZombie to find the problem, and it points me to an instance of LocationSelectionController.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
您对 NSFetchedResultsController 的处理搞砸了。首先,考虑以下代码:
如果您的
fetchedResultsController
属性声明为assign
,则此代码或多或少是正确的(尽管语义是错误的)。如果该属性被声明为retain
,则您将在此处泄漏对aFRC
的引用。但无论哪种方式,你的释放都是错误的。在 dealloc 中,您可以看到:
第一行将属性(大概是 _fetchedResultsController ivar)设置为 nil,因此第二行永远无法释放该对象。如果该属性被声明为
retain
,我猜这是为了清理上面提到的泄漏;如果它被声明为分配
,这就是上面提到的不正确语义的证据。即使这“正确”工作,viewDidUnload 中的版本也没有额外的版本,所以事情仍然是错误的。无论哪种方式,泄漏的引用都会使 NSFetchedResultsController 保持活动状态。最终它发现了一个更改,并尝试将其发送给其委托。当然,这会失败,因为委托早已被释放。
您很可能永远不需要从此类的实现外部分配 fetchedResultsController。那么你真正应该做的是这样的:
属性
fetchedResultsController
应该声明为:你现有的
fetchedResultsController
很好,除了它应该分配_fetchedResultsController< /code> ivar 直接而不是尝试分配 self.fetchedResultsController
。然后 dealloc 和 viewDidUnload 方法应该分别释放该对象,如下所示:
为了更加确定,您还可以在释放它之前将 _fetchedResultsController.delegate 设置为 nil ,但文档并未表明这是必要的,因为对于其他一些类也是如此。
Your handling of the NSFetchedResultsController is screwed up. First, consider this code:
If your
fetchedResultsController
property is declaredassign
, this code is more or less correct (although then the semantics are wrong). If the property is declaredretain
, you're leaking a reference toaFRC
here.But either way, your deallocation is wrong. In dealloc, you have this:
The first line sets the property (and this presumably the
_fetchedResultsController
ivar) to nil, so the second line can never release the object. If the property is declaredretain
, I guess this is an attempt to clean up the leak mentioned above; if it is declaredassign
, this is evidence of the incorrect semantics mentioned above. And even if this worked "correctly", the version inviewDidUnload
doesn't have that extra release so things are still wrong.Either way, the leaked reference leaves the NSFetchedResultsController alive. Eventually it finds a change, which it tries to send to its delegate. And that, of course, fails because the delegate has long since been deallocated.
Chances are good that you never need to assign the fetchedResultsController from outside the implementation of this class. What you really should do then is something like this:
The property
fetchedResultsController
should be declared as:Your existing
fetchedResultsController
is fine, except that it should assign the_fetchedResultsController
ivar directly instead of trying to assignself.fetchedResultsController
.Then the dealloc and viewDidUnload methods should each release the object something like this:
You could also set
_fetchedResultsController.delegate
nil before releasing it, to be extra sure, but the documentation doesn't indicate this is necessary as it does for some other classes.我遇到了类似的问题,但结果不同。
就我而言,控制器中有一些 KVO 观察者,但在 dealloc 中没有相应的 removeObserver:forKeyPath: 调用。
I had a similar problem with a different outcome.
In my case, I had some KVO observers in the controller that did not have corresponding removeObserver:forKeyPath: calls in dealloc.