调试 UIKit 崩溃问题 [UINavigationController initWithRootViewController]

21

Xcode 7.0.1

更新:

我最近尝试的最新事情是将UINavigationController的创建分解如下:

  self.viewController = [[ProjectsViewController alloc] initWithNibName:@"ProjectsViewController_iPhone" bundle:nil];
  self.navigationController = [[UINavigationController alloc] init];
  [self.navigationController setViewControllers:@[self.viewController]];
  self.window.rootViewController = self.navigationController;
  [self.window makeKeyAndVisible];

现在这样做会导致崩溃

[self.window makeKeyAndVisible];
但是痕迹完全相同。
我还尝试过在一个原始的ViewController中通过更改...
[self.navigationController setViewControllers:@[[[UIViewController alloc] init]]];

同样的结果...

原帖:

我遇到了一个我难以理解的崩溃 - 这是lldb跟踪信息:注意索引为2147483648

*** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayM removeObjectAtIndex:]: index 2147483648 beyond bounds [0 .. 2]'
*** First throw call stack:
(
0   CoreFoundation                      0x035eaa94 __exceptionPreprocess + 180
1   libobjc.A.dylib                     0x03084e02 objc_exception_throw + 50
2   CoreFoundation                      0x034f92ed -[__NSArrayM removeObjectAtIndex:] + 445
3   UIKit                               0x018c20b2 -[UIView(Hierarchy) bringSubviewToFront:] + 260
4   UIKit                               0x0193daeb -[UINavigationBar layoutSubviews] + 3692
5   UIKit                               0x018d716b -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 813
6   libobjc.A.dylib                     0x03099059 -[NSObject performSelector:withObject:] + 70
7   QuartzCore                          0x0096e60c -[CALayer layoutSublayers] + 144
8   QuartzCore                          0x0096228e _ZN2CA5Layer16layout_if_neededEPNS_11TransactionE + 388
9   QuartzCore                          0x00970b2c -[CALayer(CALayerPrivate) layoutBelowIfNeeded] + 44
10  UIKit                               0x018c4dca -[UIView(Hierarchy) layoutBelowIfNeeded] + 1244
11  UIKit                               0x01a117cf __74-[UINavigationController _positionNavigationBarHidden:edge:initialOffset:]_block_invoke + 36
12  UIKit                               0x018caca6 +[UIView(Animation) performWithoutAnimation:] + 82
13  UIKit                               0x01a1178d -[UINavigationController _positionNavigationBarHidden:edge:initialOffset:] + 922
14  UIKit                               0x01a1194c -[UINavigationController _positionNavigationBarHidden:edge:] + 326
15  UIKit                               0x01a12d5f -[UINavigationController _positionNavigationBarHidden:] + 49
16  UIKit                               0x01a1104a -[UINavigationController setNavigationBar:] + 1224
17  UIKit                               0x01a10a38 -[UINavigationController _navigationBarHiddenByDefault:] + 156
18  UIKit                               0x01a10997 -[UINavigationController navigationBar] + 41
19  UIKit                               0x01a17805 -[UINavigationController loadView] + 230
20  UIKit                               0x019d3338 -[UIViewController loadViewIfRequired] + 138
21  UIKit                               0x019d3cf1 -[UIViewController view] + 35
22  UIKit                               0x01a22226 -[UINavigationController pushViewController:transition:forceImmediate:] + 615
23  UIKit                               0x01a21e27 __54-[UINavigationController pushViewController:animated:]_block_invoke + 351
24  UIKit                               0x01a21c83 -[UINavigationController pushViewController:animated:] + 786
25  UIKit                               0x01a07be2 -[UINavigationController initWithRootViewController:] + 140
26  DELETIA                             0x0012954e -[AppDelegate application:didFinishLaunchingWithOptions:] + 1214

这是一个经过一段时间的构建和运行的成熟应用程序,但在当前的XCode中会发生上述情况。

如您所见,存在对UINavigationController:initWithRootViewController的调用-以下是代码:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// deletia - non UIKit code

  self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
  self.window.tintColor = [UIColor darkGrayColor];

  self.viewController = [[ProjectsViewController alloc] initWithNibName:@"ProjectsViewController_iPhone" bundle:nil];

  self.navigationController = [[UINavigationController alloc] initWithRootViewController:self.viewController];

// deletia - but the app crashes on the above line
}

在 Stack Overflow 上查看了一些类似问题和答案后,我尝试了几个方法。

  • 我听说如果 Info.plist 中的 View controller-based status bar appearance 设置为 YES,会出现这种情况 - 所以我把它设置为 NO / YES

  • 我听说某些 UIGestureRecognizers 会引起问题 - 所以我检查了 XIB 并确保没有影响这个视图控制器的手势识别器。

  • 我听说如果根视图控制器没有完全初始化,可能会有问题 - 所以我延迟了对 UINavigationController 的调用 1 秒钟。

  • 我不信任 ProjectsViewController - 所以我将它替换为一个普通的 UIViewController,如下所示:

    self.navigationController = [[UINavigationController alloc] initWithRootViewController:[[UIViewController alloc] init]];
    

非常感谢任何洞见;无论是导致问题的原因还是调试技巧都可以帮助解决问题。


是的,因为崩溃堆栈显示UINavigationBar正在运行其“layoutSubviews”处理程序-> bringSubviewToFront-> removeObjectAtIndex。不知道为什么它想从数组中删除某些内容,但我会尝试用更多元素填充此导航栏子视图的数组。如果不太复杂,我个人会尝试重新创建XIB。 - ishahak
嗯 - 这个XIB非常简单(没有任何导航组件)- 我可以回溯到一个旧版本(说实话大约一年前的版本)并且能够正常执行 - 我检查了一下,那个XIB上没有任何更改(或者在App Delegate中除了在上述所有内容之后添加了crashalytics)。 - Damo
看起来有些东西(可能是代码)正在要求将子视图置于前面。我想这是在请求其父视图中的视图索引并请求删除它,以便将其添加到末尾。搜索必须返回NSNotFound,即2147483648(最大整数值),然后使用它进行删除。看起来你应该专注于NavigationBar和与该栏相关的任何代码。它似乎处于导航栏隐藏的位置。默认情况下是否隐藏? - Rory McKinnel
只是为了确认一下。我猜这个应用程序最初并不是从一个故事板构建的,或者说它有一个带有初始控制器的故事板?当使用故事板时,我相信你不再需要创建UIWindow的代码行,因为这部分工作已经由系统代劳了。可能没有什么问题,但还是值得检查一下。 - Rory McKinnel
正确 - 这是一个“成熟的应用程序” - 嘿,它有XIBs,我应该感激! - Damo
显示剩余8条评论
2个回答

6

我觉得你关注的是错误的代码部分。正如你所提到的,它在[self.window makeKeyAndVisible]这一行崩溃,但实际上导致崩溃的原因可能是这一行导致ProjectsViewControllerUINavigationController对象加载并呈现给用户。看着你发布的错误日志,这就是你需要调查的部分:

2   CoreFoundation                      0x034f92ed -[__NSArrayM removeObjectAtIndex:] + 445
3   UIKit                               0x018c20b2 -[UIView(Hierarchy) bringSubviewToFront:] + 260
4   UIKit                               0x0193daeb -[UINavigationBar layoutSubviews] + 3692

您可以在这里看到,iOS试图布局UINavigationBar的子视图,然后尝试删除一个可能是NSNotFound的索引对象(这将导致NSIntegerMax,与您在崩溃日志中提到的索引相匹配)。
为了进一步研究崩溃,我建议按照以下步骤操作:
1. 用没有自定义.xib文件的UIViewController实例替换您的ProjectsViewController实例。(我在您的原始帖子中读到您已经尝试过这样做,但我仍然建议这是第一步,以消除其他问题(如果有多个),并完全解决崩溃)。此时重要的是确保您没有使用您的.xib文件,因为它可能是导致崩溃的原因(如果存在任何错误链接的输出或类似问题)。
2. 您的导航栏是否显示任何项目?同样,我注意到您在评论中提到它不会,但我会再次检查一下,看看是否有代码部分在其中添加项目到栏中,在不满足条件时将其删除。如果尝试从无效索引处删除项目,则会导致崩溃。
3. 尝试搜索在初始视图控制器的加载和显示过程中特别调用removeObjectAtIndex或任何其他可能被调用的NSMutableArray相关调用的代码。在这些地方添加断点,以查看它们是否在初始加载过程中被触发。如果是这样,请尝试在那里添加一个测试,以确保您正在尝试从中删除对象的索引大于或等于零,并且小于数组的大小。这样,如果要访问的索引是NSNotFound,它至少不会导致应用程序崩溃(无论当前崩溃如何都是一种好的实践)。如果这最终解决了问题,则可以进一步调查问题并尝试理解为什么实际上会收到NSNotFound作为索引,并逻辑上修复问题。
我希望这些步骤中的其中一步能够帮助您识别和解决问题。祝你好运!

1
我同意导航栏应该是我的首要攻击目标,但不幸的是,我看不到任何我试图更改它/添加它/删除它或者与导航栏有关的任何事情... - Damo
赏金已经发布,似乎没有新的答案。看来这个问题就这样结束了! - Ky -

1

UINavigationController是一个栈,你只能推入(push)和弹出(pop)UIViewController。在初始化导航控制器时,你应该传递一个UIViewController。但是如果你不知道RootViewController,你可以这样做。

self.viewController = [[ProjectsViewController alloc] initWithNibName:@"ProjectsViewController_iPhone" bundle:nil];
  self.navigationController = [[UINavigationController alloc] init];
  [self.navigationController pushViewController:self.viewController animated:NO];
  self.window.rootViewController = self.navigationController;
  [self.window makeKeyAndVisible];

这会导致崩溃吗? - Ky -

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接