5

I have a ViewController class that has a property that is the model which I want to observe as properties on the model change. Within my model object I have a property that is periodically updated in the background of my application. As it updates, I need to execute code within my ViewController.

To do this, I create an observer on my model from within my ViewController viewDidLoad method.

[ModelObject addObserver:self 
              forKeyPath:@"State" 
                 options:NSKeyValueObservingOptionNew 
                 context:nil];

As you can see, this is nothing special and the observation method behaves as it should so long as I leave the view displayed on my screen. If I remove the above view from the parent view, I get an EXC_BAD_ACCESS error message when my ModelObject instance Mode property changes. Specifically, my app crashes at the line that is updating the Mode property and I receive the mostly useless EXC_BAD_ACCESS on the following line of code within the ModelObject instance.

//This is located in a method that periodically toggles the value of "State"
[self setState: 2];

I would assume the solution to this problem is to call [ModelObject removeObserver: self forKeyPath:@"State"] from somewhere within my ViewController when it is removed from it's parent subview array. However, I have added that line of code within my viewDidUnload method but I've found the the viewDidUnload method isn't being called. I'm not sure if that is the right place, but it needs to go some where.

What might I be doing incorrectly? I know the problem is KVO related because if I remove the observation, the application works without any problem. My model instance can toggle this value as much as it wants and my application never crashes. What should I do to be sure that my observer is removed correctly when the observing view is being removed from it's parent's subview array?

RLH
  • 15,230
  • 22
  • 98
  • 182

2 Answers2

9

I generally like to put the addObserver: and removeObserver: in viewWillAppear: and viewWillDisappear:. I find these are more reliable bookends than viewDidLoad and viewDidUnload.

Rob Napier
  • 286,113
  • 34
  • 456
  • 610
  • I second this solution. `viewDidUnload:` is called when a memory warning is received and does not correspond to `viewDidLoad:` like one may think. – Stefan Miller Feb 02 '12 at 17:35
  • This is problematic when unit-testing a view controller without adding it to a view hierarchy. – Rudolf Adamkovič May 23 '13 at 12:28
  • I would recommend calling the viewWillAppear:/DidAppear:/etc. methods as part of the unit test. I'd recommend making that a part of unit testing any VC since these are critical life-cycle methods that your VC needs to respond to correctly. (That said, I generally focus my unit testing outside of the VC. I'm not saying testing the VC is bad; I'm just saying I personally focus on the model in unit test.) – Rob Napier May 23 '13 at 13:22
  • Thanks for this answer @RobNapier, great to see answers from authoritative developers. – Ríomhaire Feb 16 '15 at 15:21
1

You should remove it in the view controller's -dealloc

jsd
  • 7,673
  • 5
  • 27
  • 47
  • 1
    You should only do this if you added yourself as an observer at object creation (`init...` and `awakeFromNib`). Otherwise you can get an imbalance. For example, `viewDidLoad` can be called many more times than `dealloc`. – Rob Napier Feb 02 '12 at 17:12
  • Yep, that's definitely true, and that's always how I do it. – jsd Feb 02 '12 at 19:44