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

Posted

技术标签:

【中文标题】在没有调用 viewDidLoad 的情况下调用了 Dealloc(删除 KVO 观察者时崩溃)【英文标题】:Dealloc called without viewDidLoad being called (crash on removing KVO observer) 【发布时间】:2015-10-13 22:18:03 【问题描述】:

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

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

使用断点,我看到viewDidLoad 从未在此第三个选项卡上调用,但是当我退出时会调用 dealloc。到底是怎么回事?我假设UITabBarController 在我进入情节提要时持有对第三个选项卡的引用,但不会“加载”该选项卡。然而,当我释放标签栏控制器时,ios 会调用 dealloc。

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

【问题讨论】:

【参考方案1】:

不要使用@try。 Objective-C 中的异常应该总是considered programmer error,并且应该是致命的。

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


视图尚未加载,因为视图仅在需要显示时才加载。


原始 KVO 可能既危险又笨重。虽然不需要回答这个问题,ReactiveCocoa 显着改善了 KVO 体验。

【讨论】:

【参考方案2】:

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

KVO 删除是有问题的,我认为您无法避免在 dealloc 中使用 @try。我建议使用KVOController:它相当容易使用,它还可以为您处理所有边缘情况。

【讨论】:

【参考方案3】:

可能找到了更好的解决方案。我在方法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;

【讨论】:

【参考方案4】:

使用标志来设置是否已设置 KVO。根据应用的状态,使用 @try 会产生内存管理问题。

【讨论】:

以上是关于在没有调用 viewDidLoad 的情况下调用了 Dealloc(删除 KVO 观察者时崩溃)的主要内容,如果未能解决你的问题,请参考以下文章

viewDidLoad与initWithNibName的调用时机

是否有任何情况可以导致在 didBecomeActive 之前调用 ViewDidLoad?

推送 viewController 时未调用 viewDidLoad

UIViewController 未调用 viewDidLoad 且未创建 UIView 导致崩溃

ViewDidLoad 没有被调用

用函数停用在 viewdidload 中声明的 nslayoutconstraint