使用键值观察以检测对象何时被释放

时间:2013-08-05 17:58:46

标签: objective-c key-value-observing

如何找出对象何时被释放?我正在监听kvo更改,但是在保留计数变为0之前对象被释放,我收到以下警告:

类MyViewController的实例0x16562be0已取消分配,而键值观察者仍在其中注册。观察信息被泄露,甚至可能被错误地附加到其他物体上。在NSKVODeallocateBreak上设置断点以在调试器中停止。这是当前的观察信息:

基本上我要做的是检测模型何时被解雇。 我不能使用Delegate ,因为呈现的viewControllers是动态的,而我的mainViewController除了它们是UIViewController的子类之外不了解它们。

[anotherViewController addObserver:self forKeyPath:@"retainCount" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionOld | NSKeyValueObservingOptionPrior context:nil];

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    // Here check for the changes and see of the new value is 0 or not
}

我也试过监听viewController的superView被改为nil

[anotherViewController.view addObserver:self forKeyPath:@"superView" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionOld | NSKeyValueObservingOptionPrior context:nil];

5 个答案:

答案 0 :(得分:7)

您只能对对象支持的键执行键值观察。你想在这里做的事情根本不可能 - 一个对象的观察者在它到达dealloc时都应该消失。您需要构建应用程序,以便在需要时保留此对象,或者在对象消失之前主动告知感兴趣的各方。

查看对象的retainCount绝不是一个好主意。只要它有用,它只对调试很有用 - 即使这样,也有更好,更可靠的工具。 retainCount的结果只是误导,并不像大多数人所期望的那样有效。注意它为0是徒劳的练习,因为保留计数为0时不存在任何对象 - 当释放保留计数为1的对象时,它将被释放,然后您不再被允许发送消息。 (事实上​​,框架字面上无法表示0保留计数,因为它是一个无法访问的状态。)

答案 1 :(得分:3)

这里有一些问题。

一个问题是你问错了问题。您打算问“在目标被解除分配之前,我如何在合适的时间取消注册我的观察者?”相反,您提到retainCount,这往往会激起人们对使用retainCount而不是dealloc的谴责帮助你做你想做的事情,这会在适当的时候注销你的观察者。

另一个问题是您的视图控制器不拥有其模型(意味着它没有对模型的强引用)。通常,您希望视图控制器拥有其模型,以防止出现这种问题。当您的视图控制器存在时,它需要一个模型来操作,因此它应该拥有该模型。当视图控制器被释放时,它应该停止观察其模型并释放它。 (如果您使用ARC,它将在viewWillDisappear:结束时自动释放模型)。如果您的视图控制器反复打开和关闭屏幕,您也可以选择在{{1}}方法中取消注册。

请注意,对象可以同时由多个其他对象拥有。如果您有多个视图控制器在同一模型上运行,它们应该都拥有该模型,这意味着它们都应该具有对该模型的强引用。

第三个问题是你(可能)直接使用KVO。内置的KVO API使用起来不是很愉快。看看MAKVONotificationCenter。当观察者或目标被解除分配时,此KVO包装器会自动取消注册观察者。

答案 2 :(得分:2)

如果您有兴趣在对象被取消分配时收到通知,您可以在dealloc中发送通知,但不要引用被取消分配的对象。

例如

[[NSNotificationCenter defaultCenter] postNotificationName:@"myclass_dealloced" \
                                      object:[NSValue valueWithPointer:self]];

但你不会想要取消引用那个指针......

仅用于调试和测试。

答案 3 :(得分:1)

尝试在dealloc期间自动取消注册观察者太晚

调用dealloc时,对象图的状态未定义。具体而言,通常不保证解除分配的顺序,并且可能经常根据异步过程和/或autorelease而改变。

虽然解除分配对象强引用的图形应该是连贯的,但是当对象被释放时它会快速改变。

对于被解除分配的对象的观察者也是如此;当对象图的解除分配发生时,观察到的对象状态可能会发生变化。当它发生变化时,它可能会导致观察者在对象图形处于不一致状态(处于解除分配状态)时触发。

你真的需要具体地将释放与观察逻辑分开。

也就是说,当您的控制器从屏幕上解除时,它应该主动关闭模型层,包括拆除任何观察者(或通知任何观察者模型层即将消失)。

答案 4 :(得分:0)

您的观察员需要在放开对象的同时取消注册通知。

例如,如果您的对象正在其中一个属性上注册通知,请在更改属性或将其设置为nil之前取消注册所有通知。

永远不应该将“挂起”通知注册到已经完全丢失的对象。如果忘记跟踪对象,如何取消注册通知?