2

I am using a UITabBarController, and my 3rd tab observes an array on a singleton data store (implemented in viewDidLoad).

Currently if I just log out (and change root view controller from App Delegate), the app will crash when dealloc is called on that 3rd tab with the message "cannot remove observer for the key path "X" because it is not registered as an observer.

Using breakpoints, I see that viewDidLoad is never called on this 3rd tab, however dealloc is being called when I sign out. What is going on? I assume the UITabBarController is holding a reference to the 3rd tab when I enter the storyboard, but does not "load" that tab. Yet iOS calls dealloc on it when I release the tab bar controller.

Should I use a boolean to track viewDidLoad execution, or try to remove the observer with a @try statement? Is there an overall better design for this?

Chris Slowik
  • 2,859
  • 1
  • 14
  • 27
Raesu
  • 310
  • 2
  • 15

4 Answers4

3

Do not use @try. Exceptions in Objective-C should always be considered programmer error, and should be fatal.

As you say, use a boolean ivar set in -viewDidLoad to avoid this.


The view has not been loaded because views are only loaded when they are required for display.


Raw KVO can be dangerous and unwieldy. While not required to answer this question, ReactiveCocoa significantly improves the KVO experience.

2

viewDidLoad is called before the view appears for the first time. UITabBarController is creating the relevant UIViewController, but the view is not loaded during creation. It is loaded on-demand, when a user visits the tab for the first time.

KVO removal is problematic, I don't think you can avoid using @try in dealloc. I would suggest to use KVOController: it's fairly easy to use and it would also handle all the edge cases for you.

Vladimir Kofman
  • 1,983
  • 21
  • 21
1

May have found an even better solution. I add the observer in the method initWithCoder:(NSCoder *)aDecoder, which is called when the parent UITabController is loaded. I am using the storyboard which may be why I need to call override this method instead of regular init. Doing this now without the need for a BOOL flag or @try and no crashing.

- (instancetype)initWithCoder:(NSCoder *)aDecoder {
    if (self = [super initWithCoder:aDecoder]) {
        [anObject addObserver:self forKeyPath:aKeyPath options:0 context:NULL];
    }
    return self;
}
Stewart Macdonald
  • 2,062
  • 24
  • 27
Raesu
  • 310
  • 2
  • 15
0

Use a flag to set whether or not KVO has been set up. Using @try can create memory management issues depending on the state of the app.

Brandon A
  • 926
  • 7
  • 6