[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)