Вопрос

Кто-нибудь знает, почему этот корень 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
Это было полезно?

Решение

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

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

Другие советы

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

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

Я верю, что self.viewController ivar создается из MainWindow.xib перед didFinishLaunchingWithOptions ему звонят.Затем предварительно заполненный код, приведенный выше, задает размер окна rootViewController.Таким образом, если в сочетании вы укажете rootViewController выход для окна в вашем файле MainWindow.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: вызывается и новая копия viewController создается.это то, что передается в несколько методов (например viewDidLoad).копия будет уничтожена позже при вызове dealloc.хорошей новостью является то, что в этой копии сохраненные выходы не настроены, поэтому вы можете использовать это как тест, чтобы узнать, следует ли вам инициализировать переменные, вызывать другие методы и, что наиболее важно, следует ли вам освобождать и уничтожать объекты во время освобождения.

Ключ на вынос:настоящий viewController будет иметь свое сохраненное IBOutlet свойства настроены.если вы используете переопределенный метод, который вызывается несколько раз, просто проверьте один из ваших сохраненных IBOutlet свойства для NULL.если они есть NULL, затем немедленно возвращайтесь.

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

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

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

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

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

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

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

Решение:

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

Подробные сведения:

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

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

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

Я узнал, что это было результатом 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 просмотр свойства:

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

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

application:didFinishLaunchingWithOptions: и applicationDidBecomeActive:

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

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

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

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

Что, если ваш код обращался к свойству view, когда оно еще не загружено, контроллер представления создаст просто пустое представление, и это может вызвать view did load случайно.

Наиболее распространенной ошибкой является доступ к свойству view во время инициализации.Может быть, какой-то инструмент доступа к свойству (setter), который вызывается xib, должен случайно получить доступ к свойству view.

Что делать, если какое-то свойство помечено IBInspectable вы должны были бы проверить isViewLoaded прежде чем применить какое-либо значение к просмотру.


-(void) setSomeProperty:(UIColor*) someColor
{
  _someColor = someColor;
  if(self.isViewLoaded) {
    // self.view causes view creation and invokes 'viewDidLoad' then the view is not ready yet.
    self.view.backgroundColor = someColor;
  }
}

-(void) viewDidLoad
{
  [super viewDidLoad]
  if(_someColor){
    self.view.backgroundColor = _someColor;
  }
}

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top