在没有调用 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