[UINavigationController 保留]:发送到已释放实例的消息

Posted

技术标签:

【中文标题】[UINavigationController 保留]:发送到已释放实例的消息【英文标题】:[UINavigationController retain]: message sent to deallocated instance 【发布时间】:2014-09-27 09:25:00 【问题描述】:

在模拟器中模拟内存警告时出现错误,我的应用程序崩溃:

我正在使用 ARC。

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions

      UIWindow *window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
     _window = window;


    [self startWithFlash];

     return YES;


- (void)startWithFlash

    [self.window.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)];

    __weak typeof (self) weakSelf = self;

     WPRSplashViewController *splashViewController = [[WPRSplashViewController alloc] initWithNibName:@"WPRSplashView" bundle:nil doneCallback:^
        [weakSelf startApplication];
    ];


     self.window.rootViewController = [[UINavigationController alloc] initWithRootViewController:splashViewController];

    [self.window makeKeyAndVisible];


- (void)startApplication
    
     WPRMainViewController *mainViewController = [[WPRMainViewController alloc] init];
     UINavigationController * controller = [[UINavigationController alloc] initWithRootViewController:mainViewController];

     self.menuController = [[PHMenuViewController alloc] initWithRootViewController:controller
                                                                   atIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]];

     self.window.rootViewController = self.menuController;

    [self.window makeKeyAndVisible];

这发生在我调用的应用程序的某处:

[((WPRAppDelegate*) [UIApplication sharedApplication].delegate) startWithFlash];

在模拟内存警告之后。

在启用 NSZombie 的情况下运行配置文件工具我得到以下跟踪:

这不是唯一发生此类崩溃的地方。在我使用 UINavigationController 作为视图控制器的包装器并将其呈现为模态视图的每个地方,在模拟内存警告后,我都会遇到此崩溃。

我在其他地方遇到了非常相似的问题,我在这里发布了另一个问题,但没有找到合理的解决方案:Similar issue

【问题讨论】:

【参考方案1】:

经过几天的调查,我终于找到了所有这些崩溃的原因,包括"ios memory warning sent to deallocated UIViewController"中描述的原因

问题来自 PHAirViewController 项目。我仍然没有找到真正的原因,但只是在PHAirViewController.m 文件中注释掉- (void)dealloc 函数就可以了。

我在运行 Instruments 检测 NSZombies 时遇到的主要问题。所有跟踪都指向系统类,如 UINavigationController、UIImagePickerViewController 等……因此,我开始为父控制器禁用 ARC。在某些地方它有所帮助,但在某些地方却没有。

经过大量的伏都教后,我发现每个类(包括系统类)都在实现 UIViewCOntroller(PHAirViewController) Category,尽管在解除它们时总是调用 - (void)dealloc 函数。

现在唯一剩下的就是理解为什么这个函数会生成 NSZombies。有趣的是,仅仅评论它的内容(只有一行:self.phSwipeHandler = nil)并没有同样的效果。

【讨论】:

【参考方案2】:

快速修复:assert([NSThread isMainThread]); 插入代码中您访问appDelegate.window.rootViewController 的各个位置。这应该用于对属性的写入和读取访问!这将揭示罪魁祸首。 appDelegate.window.rootViewController 不得从主线程以外的任何其他线程访问。

一般来说,可能有以下原因:

    您正在使用__unsafe_unretained 变量。 您正在使用 unsafe_unretained 属性。 您使用的是非 ARC 您正在同时从不同线程访问同一个变量 您正在同时从不同线程访问相同的nonatomic,非weak 属性

解决方法 1 和 2 很简单:不要再使用 unsafe_unretained

3 的解决方法是:改用 ARC。

4 和 5 的修复:改用 atomic 属性,或同步访问您的 iVar。 (请注意,您不能直接从原子属性访问 iVar,因为这会破坏原子性。)或者,仅从一个线程使用该属性,例如仅来自主线程。

在您的示例中,我假设问题 #5 适用。罪魁祸首应该是一些非主线程从 UIWindow 访问 rootViewController

【讨论】:

我已使用您的建议进行断言,但这不是问题。另外我根本没有使用线程。但我确实使用了很多块..【参考方案3】:

您很可能在代码中的某处使用了 assign 或 __unsafe_unretained 属性。委托应始终为弱类型,以便对委托对象的引用在解除分配时被取消。

另外,调用:

[((WPRAppDelegate*) [UIApplication sharedApplication].delegate) startWithFlash];

...来自您应用程序的另一个类中的味道有点臭。一个我吃过很多次的。这意味着你有循环依赖。您的应用程序委托依赖于使用此代码的类(传递,如果不是直接),并且该类依赖于您的应用程序委托。查看您的 Instruments 跟踪,您似乎在其他地方采用了委托模式,因此您对解耦有一些经验。我建议通过委托链、通知或块来冒泡该消息。

【讨论】:

以上是关于[UINavigationController 保留]:发送到已释放实例的消息的主要内容,如果未能解决你的问题,请参考以下文章

带有主 UINavigationController 和详细 UINavigationController 的 UISplitViewcontroller

带有 UINavigationController 的 UITabBarController,在 hidesBottomBarWhenPushed 上隐藏 UINavigationController

在 UINavigationController 内的 UITabBarcontroller 中添加 UINavigationController?

UINavigationController 与 AppDelegate 中的 UISegmentedControl 切换 UINavigationController 的 rootviewcontr

从一个 UINavigationController 到另一个 UINavigationController (Swift iOS 9, xcode 7)

UINavigationController