iphone - UINavigationController setViewControllers 导致应用程序崩溃

标签 iphone crash uinavigationcontroller

我遇到了一个奇怪的问题,我不知道这是我的错误(最有可能)还是 UINavigationController 中的错误。

我在我的应用程序中使用 UINavigationController。在某些情况下,我需要复杂的导航,例如“弹出 2 个屏幕并推送新的屏幕”。目前我通过获取当前navigationController.viewControllers来做到这一点,修改集合,并调用[navigationController setViewControllers:newStack animated:YES] .

这使得我的应用程序经常崩溃。通常崩溃是 SIGBUS 或 SIGSEGV。重现步骤如下:

  1. 进行复杂的导航之一
  2. 根据导航类型返回一到两次
  3. 崩溃!

有趣的是:

  • 如果导航只是“返回几个屏幕”,那么下一个返回就会崩溃。如果导航是“返回几个屏幕并推送新屏幕”,那么第二个返回将会崩溃。此外,在这种情况下,后退、前进、后退、后退等导航通常不会导致应用程序崩溃
  • 最奇怪的是:如果我这样做[navigationController popToRootViewControllerAnimated:NO]在更新堆栈之前,应用程序不会崩溃,或者至少崩溃的情况要少得多(我无法重现崩溃)。

我的信号处理程序捕获的崩溃堆栈跟踪示例:

  1. 0x0027a9 mysighandler()
  2. 0x3293d82b _sigtramp()
  3. 0x31c59065 -[UIApplication sendAction:to:from:forEvent:]
  4. 0x31c59005 -[UIApplication sendAction:toTarget:fromSender:forEvent:]
  5. 0x31c58fd7 -[UIControl sendAction:to:forEvent:]
  6. 0x31c58d31 -[UIControl _sendActionsForEvents:withEvent:]
  7. 0x31c59645 -[UIControl touchesEnded:withEvent:]
  8. 0x31c5865d -[UIWindow _sendTouchesForEvent:]
  9. 0x31c58039 -[UIWindow sendEvent:]
  10. 0x31c5492f -[UIApplication sendEvent:]
  11. 0x31c543a7 _UIApplicationHandleEvent()
  12. 0x3352c9ed PurpleEventCallback()
  13. 0x3358ac2d CFRunLoopRunSpecific()
  14. 0x3358a35d CFRunLoopRunInMode()
  15. 0x3352bb33 GSEventRunModal()
  16. 0x3352bbdf GSEventRun()
  17. 0x31c1976f -[UIApplication _run]
  18. 0x31c18473 UIApplicationMain()
  19. 0x00214d main()
  20. 0x0020c4 start()

另一个:

  1. 0x002945 mysighandler()
  2. 0x3293d82b _sigtramp()
  3. 0x31c5ead3 -[UIScrollView _updatePanWithStartDelta:event:gesture:ignoringDirectionalScroll:]
  4. 0x31c5e435 -[UIScrollView handlePan:]
  5. 0x31d14651 -[UITableView handlePan:]
  6. 0x33590da7 -[Protocol performSelector:withObject:]
  7. 0x31c428b5 -[UIGestureRecognizer _updateGestureWithEvent:]
  8. 0x31c427a9 -[UIGestureRecognizer _updateGestureStateWithEvent:afterDelay:]
  9. 0x31c583d5 -[UIWindow _sendGesturesForEvent:]
  10. 0x31c5802b -[UIWindow sendEvent:]
  11. 0x31c5492f -[UIApplication sendEvent:]
  12. 0x31c543a7 _UIApplicationHandleEvent()
  13. 0x3352c9ed PurpleEventCallback()
  14. 0x3358ac2d CFRunLoopRunSpecific()
  15. 0x3358a35d CFRunLoopRunInMode()
  16. 0x3352bb33 GSEventRunModal()
  17. 0x3352bbdf GSEventRun()
  18. 0x31c1976f -[UIApplication _run]
  19. 0x31c18473 UIApplicationMain()
  20. 0x0022e9 main()
  21. 0x002260 start()

“复杂”导航实现示例:

@implementation UINavigationController(MyCategory)
- (void)popViewControllers:(NSInteger)count {
    NSArray* oldList = self.viewControllers;
    NSMutableArray* newList = [NSMutableArray arrayWithArray:oldList];
    if(count > [oldList count]) {
        CLogError(@"Poping %d screens when there is only %d", count, [oldList count]);
        count = [oldList count] - 1;
    }
    for(int i = 0; i<count; i++) {
        [newList removeLastObject];
    }
    [self setViewControllers:newList animated:YES];
}
@end

现在有人知道我可能做错了什么吗?我已经没有主意了。

添加:

我确实使用 NSZombieEnabled 和 MallocStackLogging 运行我的应用程序来找出失败的对象。然而它并没有给出我合理的结果。对于堆栈跟踪 #1,它在步骤 3 失败 ( -[UIApplication sendAction:to:from:forEvent:] ) 并且僵尸对象是 -[UIBarButtonItem performSelector:withObject:withObject:]: message sent to deallocated instance 0xa5f5f90 。这是应用程序从 2 个屏幕返回的屏幕的右侧导航栏按钮(请记住,此 2 个屏幕返回导航有效,只有下一个“通常”返回导航失败)。但我没有用那个按钮做任何事情。对应代码在ViewControler的initWithSomething:(Something*)something是:

UIBarButtonItem* doneItem = [[UIBarButtonItem alloc] initWithTitle:@"Complete"
                                                             style:UIBarButtonItemStyleDone 
                                                             target:self action:@selector(onDone)];
self.navigationItem.rightBarButtonItem = doneItem;
[doneItem release]; 

这个按钮唯一特别的地方是 onDone选择器执行 2 个屏幕后退导航,但我认为这并不重要。所以我相信更高级别的对象(可能是 View Controller 或 UINavigationController?)有问题。但出了什么问题呢?

2011 年 10 月 4 日添加:

由于人们有时仍然在搜索这个问题,这里有一些代码。我当前解决此问题的方法是使用 UINavigationController 的自定义子类,而不是通过以下 hack 来代替它(不保证这有效或仍然必要):

@interface CustomUINavigationController : UINavigationController {
}

@end


@implementation CustomUINavigationController
- (void)setViewControllers:(NSArray*)newStack animated:(BOOL)animated { 
    // HACK HACK
    // Somehow everything fails if I don't clean stack before putting new
    // But on iOS4 popToRootViewControllerAnimated might call setViewControllers:animated
    // let's avoid call stack overflow
    static int stackCount = 0;
    if(!stackCount++) {
        if([self.viewControllers count] != 1) {
            [self popToRootViewControllerAnimated:NO];
        }
        else {
            UIViewController* tmpVc = [[[UIViewController alloc] init] autorelease];
            NSArray* tmpStack = [NSArray arrayWithObject:tmpVc];
            [super setViewControllers:tmpStack animated:NO];                
        }
    }
    [super setViewControllers:newStack animated:animated];
    stackCount--;
}
@end

另一件重要的事情:当之前的动画导航仍在进行时,最好不要启动动画导航(即至少在调用 viewWillAppear: 之前)。

最佳答案

我认为在僵尸模式下通过 Instruments 运行它是值得的。这几乎肯定是一个内存问题,因为某些东西访问了已经释放的对象。

关于iphone - UINavigationController setViewControllers 导致应用程序崩溃,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2357698/

相关文章:

iphone - 调用 Superview 的 UILongPressGestureRecognizer 而不是 Subview

iphone - 语义问题 : Use of undeclared identifier

iphone - 如何自动关闭UIActionSheet

java - 我应该如何诊断和防止 JVM 崩溃?

ios - 几天后正在测试的 iPhone 应用程序崩溃

ios - 如何删除和添加导航栏上的 "back button"

iphone - 对于 "Finishing up a navigation transition in an unexpected state. Navigation Bar subview tree might get corrupted."该怎么办

Android KitKat multidex 应用程序崩溃

ios - 在不使用内置容器的情况下实现 View Controller 容器?

ios - 查看旧导航栏和选项卡栏 Controller 的 Controller 问题