Подтвердить что ты не робот

ViewDidLoad получает вызов дважды на rootViewController при запуске

Кто-нибудь знает, почему этот корень View Controller's viewDidLoad вызывается дважды при запуске? Это сводит меня с ума!

здесь трассировка стека с первого раза через viewDidLoad:

#0  0x0000276a in -[RootViewController viewDidLoad] at RootViewController.m:71
#1  0x3097548f in -[UIViewController view]
#2  0x00002734 in -[RootViewController initWithCoder:] at RootViewController.m:39
#3  0x30ab5ce4 in -[UIClassSwapper initWithCoder:]
#4  0x30514636 in _decodeObjectBinary
#5  0x30514035 in _decodeObject
#6  0x30ab5a1d in -[UIRuntimeConnection initWithCoder:]
#7  0x30514636 in _decodeObjectBinary
#8  0x30515f27 in -[NSKeyedUnarchiver _decodeArrayOfObjectsForKey:]
#9  0x305163b0 in -[NSArray(NSArray) initWithCoder:]
#10 0x30514636 in _decodeObjectBinary
#11 0x30514035 in _decodeObject
#12 0x30ab4dde in -[UINib instantiateWithOptions:owner:loadingResourcesFromBundle:]
#13 0x30ab6eb3 in -[NSBundle(NSBundleAdditions) loadNibNamed:owner:options:]
#14 0x308f85f1 in -[UIApplication _loadMainNibFile]
#15 0x30901a15 in -[UIApplication _runWithURL:sourceBundleID:]
#16 0x308fef33 in -[UIApplication handleEvent:withNewEvent:]
#17 0x308fad82 in -[UIApplication sendEvent:]
#18 0x309013e1 in _UIApplicationHandleEvent
#19 0x32046375 in PurpleEventCallback
#20 0x30245560 in CFRunLoopRunSpecific
#21 0x30244628 in CFRunLoopRunInMode
#22 0x308f930d in -[UIApplication _run]
#23 0x309021ee in UIApplicationMain
#24 0x000022e4 in main at main.m:14

и второй раз:

#0  0x0000276a in -[RootViewController viewDidLoad] at RootViewController.m:71
#1  0x30ab50cd in -[UINib instantiateWithOptions:owner:loadingResourcesFromBundle:]
#2  0x30ab6eb3 in -[NSBundle(NSBundleAdditions) loadNibNamed:owner:options:]
#3  0x308f85f1 in -[UIApplication _loadMainNibFile]
#4  0x30901a15 in -[UIApplication _runWithURL:sourceBundleID:]
#5  0x308fef33 in -[UIApplication handleEvent:withNewEvent:]
#6  0x308fad82 in -[UIApplication sendEvent:]
#7  0x309013e1 in _UIApplicationHandleEvent
#8  0x32046375 in PurpleEventCallback
#9  0x30245560 in CFRunLoopRunSpecific
#10 0x30244628 in CFRunLoopRunInMode
#11 0x308f930d in -[UIApplication _run]
#12 0x309021ee in UIApplicationMain
#13 0x000022e4 in main at main.m:14
4b9b3361

Ответ 1

Weird. Я не видел этого конкретного случая, но в целом вы должны предположить, что viewDidLoad можно вызвать несколько раз. Он будет вызван, когда будет загружен файл nib, который ссылается на этот контроллер.

Для простого приложения с одним ножом это не должно происходить. Но в более сложном приложении, которое может загружать и выгружать контроллеры представлений, это происходит все время.

Ответ 2

У меня была эта же проблема, когда мое приложение было впервые запущено. Я обнаружил, что в моем файле MainWindow.xib я установил как мою App Delegate viewController, так и мою Window rootViewController на мой контроллер корневого представления. Когда вы создаете файл проекта View Based в Xcode, ваш App Delegate didFinishLaunchingWithOptions будет предварительно заполнен:

self.window.rootViewController = self.viewController;
[self.window makeKeyAndVisible];
return YES;

Я считаю, что self.viewController ivar создается из MainWindow.xib до вызова didFinishLaunchingWithOptions. Затем предварительно заполненный код устанавливает окно rootViewController. Поэтому, если в связи с этим вы указываете выход rootViewController для окна в вашем файле MainWindow.xib, ваш контроллер корневого представления фактически будет создан дважды и дважды добавлен в качестве контроллера представления корневого окна Windows.

Ответ 3

Я сделал некоторую отладку, и вот что я нашел о заказе загрузки ViewController:

initWithNibName:bundle:     self = <original instance>, retainedOutlet = 0x0  
loadView >>>                self = <original instance>, retainedOutlet = 0x0  
      initWithCoder:        self = <coder instance>,    retainedOutlet = 0x0  
      initWithCoder:        self = <coder instance>,    retainedOutlet = 0x0  
      setView:              self = <original instance>, retainedOutlet = 0x0  
      setRetainedOutlet:    self = <original instance>, retainedOutlet = 0x1613c40  
      viewDidLoad           self = <coder instance>,    retainedOutlet = 0x0  
      awakeFromNib          self = <coder instance>,    retainedOutlet = 0x0  
loadView <<<  
viewDidLoad                 self = <original instance>, retainedOutlet = 0x1613c40  
viewWillAppear:             self = <original instance>, retainedOutlet = 0x1613c40  
dealloc                     self = <coder instance>,    retainedOutlet = 0x0
viewDidAppear:              self = <original instance>, retainedOutlet = 0x1613c40

Во время метода loadView вызывается initWithCoder: и создается новая копия ViewController. это то, что передается нескольким из методов (например, viewDidLoad). копия будет уничтожена позже в вызове dealloc. хорошей новостью является то, что в этой копии сохраненные выходы не настроены, поэтому вы можете использовать это как тест, чтобы знать, нужно ли инициализировать переменные, вызвать другие методы и, что самое важное, если вы должны освобождать и уничтожать объекты во время dealloc.

Key takeaway: реальный ViewController будет иметь сохраненные свойства IBOutlet. если вы находитесь в переопределенном методе, который вызывается несколько раз, просто проверьте один из ваших сохраненных IBOutlet свойств для NULL. если они NULL, то немедленно вернуться.

Кто-нибудь узнал, почему это происходит так?

Побочный эффект: вы не можете надежно использовать awakeFromNib.

Ответ 4

Вы не можете предположить, что viewDidLoad будет вызываться только один раз. Если вы инициализируете объекты и хотите, чтобы гарантировать инициализацию либо в методе init, либо если вы загружаете файл nib из метода awakeFromNib.

Ответ 5

У меня была аналогичная проблема, и это было результатом переименования моего XIB файла и его класса ViewController (File Owner). Не делайте этого - поскольку это действительно привело к тому, что мнения и делегаты были неверно определены внутри XML, и он не был восстановлен. Между тем, у меня была ссылка на загрузку оригинального VC, который должен был стать моим новым VC. Я считаю, что родитель заново воссоздал себя, а затем VC я действительно пытался вызвать. В принципе, я создал косвенную рекурсию для VC, которая имеет x2 viewDidLoad записи в моем трассировке.

Я не думаю, что есть какая-то веская причина для x2 viewDidLoad, поскольку она является генезису и может вызывать другую инициализацию с неправильными предполагаемыми предпосылками. Каждый раз, когда я видел x2 viewDidLoad, это была ошибка кодирования с моей стороны - довольно часто, когда я был рефакторинг и перемещение классов VC вокруг.

Если есть веская причина больше, чем на вызов viewDidLoad, пожалуйста, кто-то (Apple Dev вы слушаете) объясните это технически - я искал этот ответ уже несколько месяцев.

Ответ 6

У меня была эта проблема, но я смог ее исправить.

Решение

Переименуйте класс контроллера представления, который загружается дважды.

Подробнее

Переименуйте его и сделайте новое имя чем-то совершенно новым. Переименование файла не останавливает проблему с загрузкой-дважды. Создание нового проекта (как утверждают другие) может быть излишним, по крайней мере, сначала попробуйте более простые решения! Переименуйте класс целевого VC.

Подсказка: Если переименование класса исправляет вашу проблему, вам, очевидно, придется обновить все ваши ссылки на этот класс. Вы можете ускорить это, используя Command + Shift + F для поиска по всему проекту.

Ответ 7

Я столкнулся с той же проблемой, что и перепроектирование ViewController с нуля, чтобы избавиться от XIB файла и сделать класс повторно используемым. У меня был второй экземпляр ViewController, который получит сообщение viewDidLoad, за которым следует сообщение dealloc.

Я узнал, что это был результат метода loadView, который не переопределяется в ViewController. Значение по умолчанию loadView называется awakeFromNib, а свойство nibName - имя класса. Несмотря на то, что я удалил XIB файл из проекта, он все еще находился в каталоге приложения на симуляторе.

Итак, хотя вы могли бы просто reset содержимое и настройки симулятора избавиться от второго viewDidLoad, лучшим способом может быть просто переопределить loadView следующим образом:

- (void)loadView {
    self.view = [[[UIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]] autorelease];
    self.view.autoresizingMask = UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleBottomMargin; 
}

Это имеет смысл, если вы рассматриваете документацию для свойства представления UIViewController's:

Если вы получаете доступ к этому свойству и его значение в настоящее время равно нулю, представление контроллер автоматически вызывает loadView и возвращает результат. По умолчанию loadViewметод пытается загрузить представление из файл nib, связанный с представлением контроллер (если есть). Если ваш взгляд контроллер не имеет ассоциированного nib, вы должны переопределить loadView и использовать его для создания корневого представления и всех его подзонов.

Ответ 8

В моем случае я не заметил, что я фактически назначил rootViewController дважды:

application:didFinishLaunchingWithOptions: и applicationDidBecomeActive:

Ответ 9

Чтобы добавить к этому, если вы используете системную функцию, такую ​​как TouchID, тогда applicationWillResignActive в вашем AppDelegate будет вызван, и если вы скажете, сбросьте контроллеры на защищенный корневой контроллер, тогда вы получите reewoked, и выполнитьSegueWithIdentifier (self.MAIN_SEGUE, отправитель: self) не будет срабатывать!

Ответ 10

Это случилось со мной, когда я объединил проект из раскадровки на старый, используя xibs для построения представлений. Основной причиной отказа было то, что я не мог правильно правильно подобрать модальный вид. Как обычно я делаю это, имея метод делегата из конструкции UIButton, экземпляр определенного диспетчера представлений, задайте некоторые его свойства (самый импортный из них является делегатом, поэтому я могу корректно отключить контроллер модального представления), а затем представить это модально. В новом раскадровке это, предположительно, сделано с помощью segue. Настройка перехода возможна только путем создания пользовательского класса, который расширяет класс UIStoryboardSegue. Я считаю, что слишком много хлопот по сравнению с простым способом, которым это было так, чтобы я слился.

Как это заставило меня дважды загружать диспетчер представлений? При передаче кода из проекта раскадровки в проект xib я сделал пару xib (по одному для каждого ViewController) и скопировал объект viewcontroller из раскадровки. Это привело к тому, что xib был в нем не вилым, а viewcontroller; что я поставил viewcontroller в viewcontroller (поскольку владелец файла также является экземпляром viewcontroller). Я не думаю, что в вашем случае у вас была эта проблема, но я надеюсь, что это может помочь кому-то в свое время.

Чтобы исправить это перемещение, вид из контроллера представления из контроллера просмотра и на корневой уровень раздела объектов. Как контроллер вида, так и его навигационный элемент следует удалить. Создайте и запустите, и вы увидите только одно распределение для контроллера вида. Это владелец файла.