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

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

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

我正在使用 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

【问题讨论】:

    标签: ios objective-c automatic-ref-counting


    【解决方案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 不得从主线程以外的任何其他线程访问。

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

      1. 您正在使用__unsafe_unretained 变量。
      2. 您正在使用 unsafe_unretained 属性。
      3. 您使用的是非 ARC
      4. 您正在同时从不同线程访问同一个变量
      5. 您正在同时从不同线程访问相同的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 跟踪,您似乎在其他地方采用了委托模式,因此您对解耦有一些经验。我建议通过委托链、通知或块来冒泡该消息。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-05-23
        • 2011-07-15
        相关资源
        最近更新 更多