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

10 ответов


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

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


у меня была такая же проблема, когда мое приложение было впервые запущено. То, что я нашел, было в моем главном окне.xib-файл, я устанавливал оба делегата моего приложения viewController выход, и мое окно rootViewController выход к моему контроллеру корневого представления. Когда вы создаете файл проекта на основе представления в Xcode, делегат вашего приложения didFinishLaunchingWithOptions будет предварительно заполнено:

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

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


Я сделал некоторую отладку, и вот что я нашел о 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: называется и новая копия!-Создан -3--> - это. это то, что передается в несколько методов (например,viewDidLoad). копия уничтожается позже в вызове dealloc. хорошей новостью является то, что в этой копии сохраненные розетки не настроены, поэтому вы можете использовать это как тест, чтобы узнать, следует ли инициализировать переменные, вызывать другие методы и большинство важно, если вы должны освободить и уничтожить объекты во время dealloc.

ключ на вынос: реальный viewController придется ее сохранить IBOutlet свойства настроен. если вы находитесь в переопределенном методе, который вызывается несколько раз, просто проверьте один из сохраненных IBOutlet свойства NULL. если это так!--8-->, тут же вернуться.

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

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


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


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

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

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


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

решение:

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

подробности:

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

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


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

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

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

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

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

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


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

application:didFinishLaunchingWithOptions: и applicationDidBecomeActive:


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


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

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

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