Monday, February 1, 2010

popViewControllerAnimated & message sent to deallocated instance

This is just an informational posting -- it is so odd I thought I'd share.

If you have any of the following:

  • controllerDidChangeContent

  • didChangeSection

  • didChangeObject

  • controllerWillChangeContent

in the same class as a save that does a
when the save completes you will get a

controllerWillChangeContent:]: message sent to deallocated instance (insert your address here)

I'm not exactly sure how this happens
but essentially
these methods hold onto the old copy of self in some manner.

Here's an example:

(gdb) p self
$1 = (GroupItemSelectViewController *) 0x3a34f70
Current language: auto; currently objective-c
(gdb) c

Now pop back to the old screen and then do some editing:

2010-01-05 20:34:36.052 GoodToGo[62202:207] (GroupItemSelectViewController:didSelectRowAtIndexPath) setting Item to YES
2010-01-05 20:34:37.960 GoodToGo[62202:207] (GroupController:numberOfRowsInSection) rows: 3
2010-01-05 20:34:39.902 GoodToGo[62202:207] (GroupController:numberOfRowsInSection) rows: 2
2010-01-05 20:34:42.690 GoodToGo[62202:207] (GroupController:numberOfRowsInSection) rows: 3
2010-01-05 20:34:43.561 GoodToGo[62202:207] (didSelectRowAtIndexPath) Selecting row 0
2010-01-05 20:34:43.563 GoodToGo[62202:207] (GroupItemSelectViewController:viewDidLoad) fetchedItemCount:5, holderCount: 5

What is the current value of self?

(gdb) p self
$2 = (GroupItemSelectViewController *) 0x3c84820
(gdb) c
2010-01-05 20:34:51.690 GoodToGo[62202:207] (GroupItemSelectViewController:didSelectRowAtIndexPath): setting Item to NO
2010-01-05 20:34:53.546 GoodToGo[62202:207] (GroupController:numberOfRowsInSection) rows: 3
2010-01-05 20:34:53.547 GoodToGo[62202:207] *** -[GroupItemSelectViewController controllerWillChangeContent:]: message sent to deallocated instance 0x3a34f70

As you can see 0x3a34f70 is the previous value of self, not its current value.

I do consider this a bug in the framework.


雙魚 said...
This comment has been removed by a blog administrator.
Patrick Alessi said...

I am seeing exactly the same issue and have been really struggling with it all day. Ugh!

In my case, I am using a fetched results controller with Core Data, and the class is a NSFetchedResultsControllerDelegate.

When I save to the context, Core Data sends controllerDidChangeContent to the delegate which happens to be self. This works fine the first time through, but the second time through, I get message sent to deallocated instance on self.

Did you resolve the issue, or submit a bug report to Apple?

Patrick Alessi said...

Like you, I am not sure why this is happening. However, I have a solution. I just set the fetched results controller delegate to nil before I pop the controller. Then, the next time through, the delegate gets set to the new self address properly.

rdf said...

I resolved it -- basically the answer is "don't mix the two approaches".
The change methods are used at top level view controller, while the pop is used in the subordinate controller views.

The recipes example project shows how to make it work.