视图控制器正在发送一条消息,即使它已被释放

我不确定在iPhone SDK 3.0中有什么改变,但我得到最奇怪的错误。 我有一个视图控制器层次结构,我根据界面方向在视图控制器之间切换。 从我可以告诉,错误是造成每当我旋转接口已被释放视图控制器正在发送一个shouldAutorotateToInterfaceOrientation消息。 这是错误的回溯:

#0 0x01dc43a7 in ___forwarding___ #1 0x01da06c2 in __forwarding_prep_0___ #2 0x002e6733 in -[UIWindow _shouldAutorotateToInterfaceOrientation:] #3 0x002e6562 in -[UIWindow _updateToInterfaceOrientation:duration:force:] #4 0x002e6515 in -[UIWindow _updateInterfaceOrientationFromDeviceOrientation] #5 0x0004d63a in _nsnote_callback #6 0x01d8f005 in _CFXNotificationPostNotification #7 0x0004aef0 in -[NSNotificationCenter postNotificationName:object:userInfo:] #8 0x0045b454 in -[UIDevice setOrientation:] #9 0x002d6890 in -[UIApplication handleEvent:withNewEvent:] #10 0x002d16d3 in -[UIApplication sendEvent:] #11 0x002d80b5 in _UIApplicationHandleEvent #12 0x024c2ef1 in PurpleEventCallback #13 0x01d9bb80 in CFRunLoopRunSpecific #14 0x01d9ac48 in CFRunLoopRunInMode #15 0x024c17ad in GSEventRunModal #16 0x024c1872 in GSEventRun #17 0x002d9003 in UIApplicationMain #18 0x00002d50 in main at main.m:14 

使用NSZombieEnabled打印到debugging控制台的错误是:

 2009-10-18 20:28:34.404 Restaurants[12428:207] *** -[ToolbarController respondsToSelector:]: message sent to deallocated instance 0x3b2b2a0 (gdb) continue Current language: auto; currently objective-c 2009-10-18 20:31:43.496 Restaurants[12428:207] *** NSInvocation: warning: object 0x3b2b2a0 of class '_NSZombie_BeltToolbarController' does not implement methodSignatureForSelector: -- trouble ahead 2009-10-18 20:31:43.496 Restaurants[12428:207] *** NSInvocation: warning: object 0x3b2b2a0 of class '_NSZombie_BeltToolbarController' does not implement doesNotRecognizeSelector: -- abort 

我不明白的是,为什么系统试图给这个控制器发送消息,即使这个控制器已经被释放,并且有办法告诉系统控制器不再存在。

[更新]:我已经汇集了一个复制bug: 下载的示例项目

加载应用程序,然后将模拟器的方向从“横向”更改为“纵向”,并发生几次。 我已经在物理电话上尝试了相同的代码,并且其行为方式完全相同,所以这不是模拟器相关的问题。

[更新]:我已经用苹果技术团队的支持请求之一,看看他们是否能帮助我到底。 将解决scheme,如果他们有一个 – 在这里。 感谢你目前的帮助。

显然,pipe制员仍在注册有关的通知。 将-removeObserver:self发送到您的-dealloc方法中的通知中心。

所以在等待了一个星期之后,Apple开发者技术支持帮助我排除了我的问题。 这是他们的回应:

“我查看了你的代码,发现了一些你需要关心的东西,其中一些可能会导致你的问题。在你的ControllerSwitchAppDelegate.m源文件中,你实现了” didRotate “方法。设备定位通知在视图控制器级别而不是在UIApplication级别,这将使你的代码更加简单和封装,允许每个视图控制器被显示为处理自己的旋转逻辑,同时你也使用了多个视图控制器,也就是说,两个“视图”属性被添加和删除,当设备旋转。这不是使用UIKit的常见方法。想法是提出一个视图控制器(或其视图属性)在一个在不同的子视图控制器中没有父视图控制器交换,表面上你的方法似乎是可行的,但从长远来看,我推荐一种不同的方法。

我们有一个名为“AlternateViews”的示例,它可以在http://developer.apple.com/iphone/library/samplecode/AlternateViews/index.htmlfind。

在这个示例中,它几乎可以满足您的需求。 它为给定的设备方向提供了一个“备用”视图控制器。 只是提出了一个视图控制器使用“presentModalViewController”与一个名为“modalTransitionStyle”,这将给你一个淡入淡出效果的过渡属性。

我最终做的是使用一个超级视图控制器来呈现和解散视图控制器。 而不是交换视图控制器和使用AppDelegate删除子视图。

这里有很多困惑…让我们看看是否有帮助:

我不明白的是,为什么系统试图给这个控制器发送消息,即使这个控制器已经被释放,并且有办法告诉系统控制器不再存在。

取消分配会破坏对象的实例,但不会销毁对实例的引用。 启用僵尸检测后,释放会导致运行时将僵尸replace为实例。 僵尸然后检测并logging消息。

发生这种情况的原因是因为该对象已经被释放,而没有从应用程序的对象图中删除对该对象的所有引用。 在这种情况下,看起来解除分配的控制器从来没有从UIWindow实例中被设置为控制器。

也就是说, 通知的处理是一个红色的鲱鱼 。 在这个回溯中,通知已经被传递,UIWindow正在处理它。 因此,问题出在UIWindow和你的应用程序之间。 最有可能的是,您的对象已被释放, 然后作为其控制器或委托从窗口中删除。

在你的程序真正完成一个对象之前 – 在发送最后一次-release调用之前,平衡最后一个现有的 – -retain你的应用程序引起或调用 – 你必须确保所有对该对象的弱引用被销毁。 例如,如果您的对象是UIWindow的委托,请确保在发送最后一个-release之前将窗口的委托设置nil (或其他对象)。

现在,在这种情况下,也可能只是您已经过度释放对象。 你可能仍然需要这个对象,但是在你完成之前,某个地方的额外-release或者-release -autorelease会导致它被破坏。

如果有人仍然在意,一个简单的解决scheme是创build一个从不改变的根视图控制器+视图。

给定SomeViewController + SomeView A和SomeViewController + SomeView B,如果将视图A作为子视图添加到窗口,则将视图B添加为子视图并删除视图A,则应用程序将在旋转时崩溃。

为了防止崩溃,创build一个通用的UIViewController + UIView X,并将视图X作为子视图添加到窗口中。 将视图A和B添加到视图X或从视图X中移除,而不是直接添加到窗口。 该应用程序将不再崩溃。

请注意,将视图添加到窗口是不够的。 您必须添加具有视图控制器的视图。

这是释放它之后设置ToolbarController = nil一个很好的理由。 将消息发送到零是安全的,但不是释放对象的地址。 在这种情况下,您将消息发送到不会退出的对象的地址,这会导致崩溃。

在发送消息之前检查ToolbarController != nil是浪费时间,因为如果它是零,则可以安全地发送消息。 如果它不是零和有效的,那么它将返回YES或NO。 但是,如果它是一个指向释放内存的指针(比如你似乎在这里),它将会崩溃。

我也遇到了这个问题。 事件的顺序是:

(1)创build应用程序的单个UIWindow对象

(2)将由视图控制器pipe理的子视图添加到窗口

(3)删除第一个视图并添加一个新视图

如果之后再旋转设备,应用程序会因发送到释放视图控制器的消息而崩溃。 (嗯,它实际上是发送到第一个视图控制器的子控制器。)它试图发送 – [respondsToSelector:@selector(shouldAutorotateToInterfaceOrientation :)]。

如果您的应用程序只能以纵向模式运行,则可以通过向UIWindow添加一个覆盖_shouldAutorotateToInterfaceOrientation的类别来解决问题:除纵向模式以外的任何其他操作都不返回NO。

显然,这不是一个真正的解决scheme。 我已经双重检查了我的代码,我发现没有理由为什么窗口应该将这个消息发送给控制器,以便从屏幕上移除并释放该视图。 这个问题似乎也出现在3.0而不是之前。 也许我在做一些愚蠢的事情,但在这里工作似乎确实有些奇怪。

我也有同样的问题,但不能像马克·史密斯的build议留下一个控制器。 用autorelease而不是release或者retain属性去除视图控制器似乎是个诀窍。

似乎父UIWindow /框架需要视图控制器挂起一段时间,以允许它删除代表链接。

我一直在遇到同样的问题,直到删除了一些我用来推动animation的'​​碰撞'代码行:

UIView* superv = navigationController.view.superview;

[navigationController.view removeFromSuperview];

[superv addSubview:navigationController.view];

当然,以上是自从苹果公司发布3.0 SDK以来“打破”的方式。 我一直被迫使用push / pop方法。 2.x也没有问题。 确保你的代码中没有类似的东西。

希望能帮助到你。

我已经在这里发布了一个答案:

https://stackoverflow.com/a/19237139/539149

我有一个地方说:

 [viewController release]; viewController = NULL; 

这导致释放被调用两次(所以内存立即被释放),但是直到iOS拥有的对象试图在主线程中稍后引用该对象才显示僵尸。