在没有调用viewDidLoad的情况下调用Dealloc(移除KVO观察者时崩溃)



我使用的是UITabBarController,我的第三个选项卡观察一个单例数据存储(在viewDidLoad中实现)上的数组。

目前,如果我只是注销(并从App Delegate更改根视图控制器),当dealloc在第3个选项卡上被调用时,应用程序将崩溃,消息为"不能删除关键路径"X"的观察者",因为它没有注册为观察者。

使用断点,我看到viewDidLoad从未在这第三个选项卡上被调用,然而dealloc在我退出时被调用。发生了什么事?当我进入故事板时,我假设UITabBarController持有对第三选项卡的引用,但不"加载"该选项卡。当我释放标签栏控制器时,iOS会调用dealloc。

我应该使用布尔值来跟踪viewDidLoad的执行,还是尝试用@try语句删除观察者?有没有更好的整体设计?

不要使用@try。Objective-C中的异常应该被认为是程序员的错误,并且应该是致命的。

正如你所说,使用在-viewDidLoad中设置的布尔变量来避免这种情况。


视图没有被加载,因为视图只有在需要显示时才会被加载。


原始KVO可能是危险和笨拙的。虽然不需要回答这个问题,但ReactiveCocoa显着改善了KVO体验。

viewDidLoad在视图第一次出现之前被调用。UITabBarController正在创建相关的UIViewController,但是在创建过程中没有加载视图。它是按需加载的,当用户第一次访问选项卡时。

KVO移除是有问题的,我不认为你可以避免在dealloc中使用@try。我建议使用KVOController:它相当容易使用,它也会为你处理所有的边缘情况。

可能找到了更好的解决方案。我在方法initWithCoder:(NSCoder *)aDecoder中添加了观察者,该方法在加载父UITabController时调用。我正在使用故事板,这可能是为什么我需要调用覆盖这个方法,而不是常规的init。现在这样做,不需要BOOL标志或@try,也不会崩溃。

- (instancetype)initWithCoder:(NSCoder *)aDecoder {
    if (self = [super initWithCoder:aDecoder]) {
        [anObject addObserver:self forKeyPath:aKeyPath options:0 context:NULL];
    }
    return self;
}

使用一个标志来设置是否设置了KVO。使用@try会产生内存管理问题,这取决于应用程序的状态。

最新更新