UINavigationController setViewControllers вызывает сбой приложения - PullRequest
4 голосов
/ 01 марта 2010

У меня странная проблема, и я не знаю, является ли это моей ошибкой (наиболее вероятно) или ошибкой в ​​UINavigationController.

Я использую UINavigationController в своем приложении.В некоторых случаях мне нужны сложные навигации, такие как «выскочить 2 экрана и нажать новый».В настоящее время я делаю это, получая текущую navigationController.viewControllers, изменяя коллекцию и вызывая [navigationController setViewControllers:newStack animated:YES].

. Оказывается, это очень часто приводит к сбою приложения.Обычно происходит сбой SIGBUS или SIGSEGV.Действия для воспроизведения выглядят следующим образом:

  1. Выполните одну из этих сложных навигаций
  2. Перейдите назад один или два раза в зависимости от вида навигации
  3. Crash!

Интересно то, что:

  • Если бы навигация была просто «несколькими экранами назад», то следующий назад был бы сбой.Если бы навигация была «на несколько экранов назад и выдвигать новый экран», то второй назад был бы сбой.Более того, в таких случаях навигация назад, вперед, назад, назад обычно не приводит к сбою приложения
  • Самое странное: если я сделаю [navigationController popToRootViewControllerAnimated:NO] перед обновлением стека, приложение не сбоит или, по крайней мере, сильно сбоитреже (я не смог воспроизвести аварию).

Пример трассировки стека аварии, обнаруженной моим обработчиком сигнала:

  1. 0x0027a9 mysighandler ()
  2. 0x3293d82b _sigtramp ()
  3. 0x31c59065 - [UApplication sendAction: to: from: forEvent:]
  4. 0x31c59005 - [UIApplication sendAction: toTarget: fromSender: forEvent:]
  5. 0x31c58d31 - [UIControl _sendActionsForEvents: withEvent:]
  6. 0x31c59645 - [UIControl * * * * 10 * 03165: 1065: 043: 065: 065 *): * E: 043: 065: 065 *: 031: 065: 031: 065: 031- [UIWindow _sendTouchesForEvent:]
  7. 0x31c58039 - [UIWindow sendEvent:]
  8. 0x31c5492f - [UIApplication sendEvent:]
  9. 0x31c543a7 _UIApplicationHandle 1052 (050) * 052exe 052eex: 050ex: 032: 0exe: 0exe: 0exe: 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 5 6 6 0 0 5 5 5 5 5 5 5 0 5 0 5 5 * * * * * *
  10. 10 10 0 0 0 0 5 0 0 0 0 0 052 * 0 удачи в этом вуздал бы:tCallback ()
  11. 0x3358a35d CFRunLoopRunInMode ()
  12. 0x3352bb33 GSEventRunModal ()
  13. 0x31c18473 UIApplicationMain ()
  14. 0x00214d main ()
  15. 0x0020c4 start ()

Еще один:

  1. 0x002945 mysighandler ()
  2. 0x3293d82b _sigtramp ()
  3. 0x31c5ead3 - [UIScrollView _updatePanWithStartDelta: событие: жест: игнорирование 1081] 1080al* 0x31c5e435 - [дескриптор дескриптора UIScrollView:]
  4. 0x31d14651 - [дескриптор дескриптора UITableView:]
  5. 0x33590da7 - [Выполнить выбор протокола: withObject:]
  6. 0x31c428bd_обозначение_обозначения_обозначения:] UIG1088 *
  7. 0x31c427a9 - [UIGestureRecognizer _updateGestureStateWithEvent: afterDelay:]
  8. 0x31c583d5 - [UIWindow _sendGesturesForEvent:]
  9. 0x31c580Wb 1094: U94 [UE]: отправлять * [*] * 10 * * * * * * * 109531c5492f - [UIApplication sendEvent:]
  10. 0x31c543a7 _UIApplicationHandleEvent ()
  11. 0x3352c9ed PurpleEventCallback ()
  12. 0x3358ac2d CFRunLoop * 112 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * '' * 'находит, по коду, за *,,,,,,,,, », * (на * (*), на *, на,, на, *, на,", ",", ",", ",", ",", ",", ",", "," для тебя, для с
  13. 0x3352bb33 GSEventRunModal ()
  14. 0x3352bbdf GSEventRun ()
  15. 0x31c1976f - [UIApplication _run]
  16. 0x31c18473 UIApplicationMain ()
  17. 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 экрана (и помните, что эта навигация с двумя экранами назад работает, только следующая «обычная» навигация назад не срабатывает). Но я ничего не делаю с этой кнопкой. Соответствующий код в initWithSomething:(Something*)something ViewControler:

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

Единственное, что особенное в этой кнопке, это то, что селектор onDone выполняет навигацию на 2 экрана назад, но я не думаю, что это действительно имеет значение. Поэтому я считаю, что что-то не так на объекте более высокого уровня (вероятно, View Controller или UINavigationController?). Но что не так?

Добавление 4 октября 2011 г .:

Поскольку люди все еще иногда ищут этот вопрос, вот код. Мой текущий подход к этой проблеме заключается в использовании пользовательского подкласса UINavigationController вместо него с последующим взломом (нет гарантий, что это работает или все еще необходимо):

@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 ]

2 голосов
/ 01 марта 2010

Думаю, стоило бы запустить его через инструменты в режиме зомби. Это почти наверняка проблема с памятью, когда что-то обращается к уже освобожденному объекту.

1 голос
/ 20 декабря 2010

Для второй трассировки стека у меня была такая же проблема. Я выпускал контроллер представления, который содержал представление прокрутки.

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

Как то так.

Надеюсь, это поможет даже через 9 месяцев.

1 голос
/ 02 марта 2010

Была похожая проблема. Оказывается, что, по крайней мере, в некоторых обстоятельствах действие rightBarButtonItem вызывается, когда контроллер представления выталкивается. Мое уродливое решение для этого состоит в том, чтобы удалить элемент, который нарушил работу, когда я помещаю следующий контроллер представления в стек. Затем я проверяю в viewWillAppear, если rightBarButton равен nil, и при необходимости воссоздаю кнопку.

Однако при повторном создании кнопки она будет зависать в 0, 0 в течение доли секунды, прежде чем встать на место с правой стороны панели навигации. Немного более элегантным решением было бы установить для действия кнопки значение по умолчанию 'NULL'. Это решит первую проблему, но также сломает backBarButtonItem.

Итак, чтобы подвести итог, я все еще ищу подходящее решение для этого. У меня около дня работы, чтобы связаться с Apple с этим - может даже быть ошибкой, если подумать ...

...