第二次保存 NSManagedObjectContext 时出错(已释放实例)

发布于 2024-10-31 02:24:31 字数 2648 浏览 1 评论 0原文

我已经在这里阅读了几个处理类似问题的线程,但我只是无法弄清楚我过度发布了什么。从 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.
Instruments Screen

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

℉服软 2024-11-07 02:24:31

您对 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,我猜这是为了清理上面提到的泄漏;如果它被声明为分配,这就是上面提到的不正确语义的证据。即使这“正确”工作,viewDidUnload 中的版本也没有额外的版本,所以事情仍然是错误的。

无论哪种方式,泄漏的引用都会使 NSFetchedResultsController 保持活动状态。最终它发现了一个更改,并尝试将其发送给其委托。当然,这会失败,因为委托早已被释放。


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

属性 fetchedResultsController 应该声明为:

@property (retain, readonly) NSFetchedResultsController *fetchedResultsController;

你现有的 fetchedResultsController 很好,除了它应该分配 _fetchedResultsController< /code> ivar 直接而不是尝试分配 self.fetchedResultsController

然后 dealloc 和 viewDidUnload 方法应该分别释放该对象,如下所示:

[_fetchedResultsController release];
_fetchedResultsController = nil;

为了更加确定,您还可以在释放它之前将 _fetchedResultsController.delegate 设置为 nil ,但文档并未表明这是必要的,因为对于其他一些类也是如此。

Your handling of the NSFetchedResultsController is screwed up. First, consider this code:

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

If your fetchedResultsController property is declared assign, this code is more or less correct (although then the semantics are wrong). If the property is declared retain, you're leaking a reference to aFRC here.

But either way, your deallocation is wrong. In dealloc, you have this:

self.fetchedResultsController = nil;
[_fetchedResultsController release];

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 declared retain, I guess this is an attempt to clean up the leak mentioned above; if it is declared assign, this is evidence of the incorrect semantics mentioned above. And even if this worked "correctly", the version in viewDidUnload 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:

@property (retain, readonly) NSFetchedResultsController *fetchedResultsController;

Your existing fetchedResultsController is fine, except that it should assign the _fetchedResultsController ivar directly instead of trying to assign self.fetchedResultsController.

Then the dealloc and viewDidUnload methods should each release the object something like this:

[_fetchedResultsController release];
_fetchedResultsController = nil;

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.

昨迟人 2024-11-07 02:24:31

我遇到了类似的问题,但结果不同。

就我而言,控制器中有一些 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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文