У меня странная проблема, и я не знаю, является ли это моей ошибкой (наиболее вероятно) или ошибкой в UINavigationController.
Я использую UINavigationController в своем приложении.В некоторых случаях мне нужны сложные навигации, такие как «выскочить 2 экрана и нажать новый».В настоящее время я делаю это, получая текущую navigationController.viewControllers
, изменяя коллекцию и вызывая [navigationController setViewControllers:newStack animated:YES]
.
. Оказывается, это очень часто приводит к сбою приложения.Обычно происходит сбой SIGBUS или SIGSEGV.Действия для воспроизведения выглядят следующим образом:
- Выполните одну из этих сложных навигаций
- Перейдите назад один или два раза в зависимости от вида навигации
- Crash!
Интересно то, что:
- Если бы навигация была просто «несколькими экранами назад», то следующий назад был бы сбой.Если бы навигация была «на несколько экранов назад и выдвигать новый экран», то второй назад был бы сбой.Более того, в таких случаях навигация назад, вперед, назад, назад обычно не приводит к сбою приложения
- Самое странное: если я сделаю
[navigationController popToRootViewControllerAnimated:NO]
перед обновлением стека, приложение не сбоит или, по крайней мере, сильно сбоитреже (я не смог воспроизвести аварию).
Пример трассировки стека аварии, обнаруженной моим обработчиком сигнала:
- 0x0027a9 mysighandler ()
- 0x3293d82b _sigtramp ()
- 0x31c59065 - [UApplication sendAction: to: from: forEvent:]
- 0x31c59005 - [UIApplication sendAction: toTarget: fromSender: forEvent:]
- 0x31c58d31 - [UIControl _sendActionsForEvents: withEvent:]
- 0x31c59645 - [UIControl * * * * 10 * 03165: 1065: 043: 065: 065 *): * E: 043: 065: 065 *: 031: 065: 031: 065: 031- [UIWindow _sendTouchesForEvent:]
- 0x31c58039 - [UIWindow sendEvent:]
- 0x31c5492f - [UIApplication sendEvent:]
- 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 0 0 0 0 5 0 0 0 0 0 052 * 0 удачи в этом вуздал бы:tCallback ()
- 0x3358a35d CFRunLoopRunInMode ()
- 0x3352bb33 GSEventRunModal ()
- 0x31c18473 UIApplicationMain ()
- 0x00214d main ()
- 0x0020c4 start ()
Еще один:
- 0x002945 mysighandler ()
- 0x3293d82b _sigtramp ()
- 0x31c5ead3 - [UIScrollView _updatePanWithStartDelta: событие: жест: игнорирование 1081] 1080al* 0x31c5e435 - [дескриптор дескриптора UIScrollView:]
- 0x31d14651 - [дескриптор дескриптора UITableView:]
- 0x33590da7 - [Выполнить выбор протокола: withObject:]
- 0x31c428bd_обозначение_обозначения_обозначения:] UIG1088 *
- 0x31c427a9 - [UIGestureRecognizer _updateGestureStateWithEvent: afterDelay:]
- 0x31c583d5 - [UIWindow _sendGesturesForEvent:]
- 0x31c580Wb 1094: U94 [UE]: отправлять * [*] * 10 * * * * * * * 109531c5492f - [UIApplication sendEvent:]
- 0x31c543a7 _UIApplicationHandleEvent ()
- 0x3352c9ed PurpleEventCallback ()
- 0x3358ac2d CFRunLoop * 112 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * '' * 'находит, по коду, за *,,,,,,,,, », * (на * (*), на *, на,, на, *, на,", ",", ",", ",", ",", ",", ",", "," для тебя, для с
- 0x3352bb33 GSEventRunModal ()
- 0x3352bbdf GSEventRun ()
- 0x31c1976f - [UIApplication _run]
- 0x31c18473 UIApplicationMain ()
- 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:).