UINavigationController setViewControllers使应用程序崩溃

时间:2010-03-01 17:28:08

标签: iphone crash uinavigationcontroller

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

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

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

  1. 执行其中一项复杂的导航
  2. 根据导航类型导航一到两次
  3. 崩溃!
  4. 有趣的是:

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

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

      
        
    1. 0x0027a9 mysighandler()
    2.   
    3. 0x3293d82b _sigtramp()
    4.   
    5. 0x31c59065 - [UIApplication sendAction:to:from:forEvent:]
    6.   
    7. 0x31c59005 - [UIApplication sendAction:toTarget:fromSender:forEvent:]
    8.   
    9. 0x31c58fd7 - [UIControl sendAction:to:forEvent:]
    10.   
    11. 0x31c58d31 - [UIControl _sendActionsForEvents:withEvent:]
    12.   
    13. 0x31c59645 - [UIControl touchesEnded:withEvent:]
    14.   
    15. 0x31c5865d - [UIWindow _sendTouchesForEvent:]
    16.   
    17. 0x31c58039 - [UIWindow sendEvent:]
    18.   
    19. 0x31c5492f - [UIApplication sendEvent:]
    20.   
    21. 0x31c543a7 _UIApplicationHandleEvent()
    22.   
    23. 0x3352c9ed PurpleEventCallback()
    24.   
    25. 0x3358ac2d CFRunLoopRunSpecific()
    26.   
    27. 0x3358a35d CFRunLoopRunInMode()
    28.   
    29. 0x3352bb33 GSEventRunModal()
    30.   
    31. 0x3352bbdf GSEventRun()
    32.   
    33. 0x31c1976f - [UIApplication _run]
    34.   
    35. 0x31c18473 UIApplicationMain()
    36.   
    37. 0x00214d main()
    38.   
    39. 0x0020c4 start()
    40.   

    另一个:

      
        
    1. 0x002945 mysighandler()
    2.   
    3. 0x3293d82b _sigtramp()
    4.   
    5. 0x31c5ead3 - [UIScrollView _updatePanWithStartDelta:event:gesture:ignoringDirectionalScroll:]
    6.   
    7. 0x31c5e435 - [UIScrollView handlePan:]
    8.   
    9. 0x31d14651 - [UITableView handlePan:]
    10.   
    11. 0x33590da7 - [Protocol performSelector:withObject:]
    12.   
    13. 0x31c428b5 - [UIGestureRecognizer _updateGestureWithEvent:]
    14.   
    15. 0x31c427a9 - [UIGestureRecognizer _updateGestureStateWithEvent:afterDelay:]
    16.   
    17. 0x31c583d5 - [UIWindow _sendGesturesForEvent:]
    18.   
    19. 0x31c5802b - [UIWindow sendEvent:]
    20.   
    21. 0x31c5492f - [UIApplication sendEvent:]
    22.   
    23. 0x31c543a7 _UIApplicationHandleEvent()
    24.   
    25. 0x3352c9ed PurpleEventCallback()
    26.   
    27. 0x3358ac2d CFRunLoopRunSpecific()
    28.   
    29. 0x3358a35d CFRunLoopRunInMode()
    30.   
    31. 0x3352bb33 GSEventRunModal()
    32.   
    33. 0x3352bbdf GSEventRun()
    34.   
    35. 0x31c1976f - [UIApplication _run]
    36.   
    37. 0x31c18473 UIApplicationMain()
    38.   
    39. 0x0022e9 main()
    40.   
    41. 0x002260 start()
    42.   

    “复杂”导航实施的例子:

    @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:])和zombie对象失败 -[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:之前)。

3 个答案:

答案 0 :(得分:2)

我认为通过僵尸模式的仪器运行它是值得的。这几乎肯定是一个内存问题,有些东西访问已经发布的对象。

答案 1 :(得分:1)

有类似的问题。事实证明,至少在某些情况下,在弹出视图控制器时会调用rightBarButtonItem的动作。我对此的丑陋修复是当我将下一个视图控制器推入堆栈时删除有问题的项目。然后我检查viewWillAppear,如果rightBarButton是nil,并在必要时重新创建按钮。

但是,重新创建按钮会使其在0,0暂停一小段时间,然后在导航栏的右侧弹出。稍微更优雅的修复方法是将按钮的操作设置为默认值“NULL”。这解决了第一个问题,但也会破坏backBarButtonItem。

总而言之,我仍在为此寻找合适的解决方案。我约有一天的工作与苹果公司联系 - 甚至可能是一个错误,想一想......

答案 2 :(得分:1)

对于第二个堆栈跟踪,我遇到了同样的问题。 我正在发布包含scrollview的视图控制器。

[mainScrollView addSubview:rubriqueController.view];        
[rubriqueController release]; // Comment this line

像这样。

希望它在9个月之后有所帮助。