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

AwakeFromNib, выходы и раскадровки: неправильная документация?

В соответствии с ссылка NSObject UIKit дополнений, выходные переменные должны быть установлены к моменту времени awakeFromNib (выделение все мое):

Инфраструктура nib-load отправляет сообщение awakeFromNib каждому объекту, воссозданному из архива nib, но только после того, как все объекты в архиве были загружены и инициализированы. Когда объект получает сообщение awakeFromNib, гарантировано, что все его соединения для выхода и действия уже установлены.

...

Важно. Поскольку порядок, в котором объекты создаются из архива, не гарантируется, ваши методы инициализации не должны отправлять сообщения другим объектам в иерархии. Сообщения для других объектов могут быть отправлены безопасно из метода awakeFromNib.

Как правило, вы реализуете awakeFromNib для объектов, для которых требуется дополнительная настройка, которая не может быть выполнена во время разработки. Например, вы можете использовать этот метод для настройки конфигурации по умолчанию для любых элементов управления в соответствии с предпочтениями пользователя или значениями в других элементах управления. Вы также можете использовать его для восстановления отдельных элементов управления в каком-либо предыдущем состоянии вашего приложения.

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

  • Создайте новое приложение Single View в Xcode.
  • Перетащите второй ViewController на раскадровку.
  • Дайте первой кнопке ViewController a и создайте modal segue с этой кнопки, которая отобразит второй ViewController.
  • Создайте файл класса ViewController для второго ViewController.
  • Создайте метку на втором ViewController на раскадровке и создайте для нее выход с именем someLabel в соответствующий класс ViewController.
  • Добавьте следующую реализацию awakeFromNib ко второму ViewController:

.

- (void) awakeFromNib {
    [super awakeFromNib];
    if (self.someLabel == nil) {
        NSLog(@"someLabel property is nil");
    }
    else {
        NSLog(@"someLabel property is not nil");
    }

    if (_someLabel == nil) {
        NSLog(@"_someLabel is nil");
    }
    else {
        NSLog(@"_someLabel is not nil");
    }
}
  • Запустите приложение в симуляторе и нажмите кнопку.

Когда я это сделаю, я наблюдаю следующее:

2013-07-01 09:24:35.755 test[498:c07] someLabel property is nil
2013-07-01 09:24:35.758 test[498:c07] _someLabel is nil

Как следствие этого поведения, когда мне нужно, чтобы мои ViewControllers имели некоторую логику инициализации, которая включает их выходы, мне нужно использовать взломать, как тот, который предлагается в ответе здесь, чтобы иметь возможность использовать выходы. Если я правильно понимаю документацию, тот факт, что я вынужден использовать этот хак, является ошибкой в ​​поведении UIKit, и я должен был бы поместить эту инициализацию в awakeFromNib и просто использовать торговые точки без каких-либо хаков.

Я не могу найти никаких других упоминаний об этой проблеме в Интернете, хотя, что кажется странным, учитывая, что это принципиально важная часть функциональности, которая появляется (для меня). Я также никогда не использовал фактические файлы nib, только раскадровки, поэтому мне не хватает какой-то перспективы на этом, и документация по этому материалу достаточно сложная и достаточно сложная, что, будучи новичком в iOS, я не уверен, что понял правильно. Является ли это подлинной ошибкой UIKit или я неправильно понял документацию - возможно, этот метод даже не предназначен для использования в раскадровки?

4b9b3361

Ответ 1

Короткий ответ

Контроллер вашего представления и его иерархия представлений загружаются из отдельных файлов nib во время выполнения.

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

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

Длинный ответ

Когда Xcode создает ваше приложение, оно компилирует раскадровку. Результатом является пакет (папка, которую Finder рассматривает как файл), содержащий Info.plist и набор файлов .nib. Пример из одного из моих проектов:

:; pwd
/Users/mayoff/Library/<snip>/Pinner.app/Base.lproj/Main.storyboardc
:; ll
total 80
drwxr-xr-x  10 mayoff  staff   340 May 11 22:13 ./
drwxr-xr-x   4 mayoff  staff   136 May 11 22:13 ../
-rw-r--r--   1 mayoff  staff  1700 May 11 22:13 AccountCollection.nib
-rw-r--r--   1 mayoff  staff  1110 May 11 22:13 AccountEditor.nib
-rw-r--r--   1 mayoff  staff  2999 May 11 22:13 BYZ-38-t0r-view-8bC-Xf-vdC.nib
-rw-r--r--   1 mayoff  staff   439 May 11 22:13 Info.plist
-rw-r--r--   1 mayoff  staff  7621 May 11 22:13 LqH-9K-CeF-view-OwT-Ts-HoG.nib
-rw-r--r--   1 mayoff  staff  6570 May 11 22:13 OZq-QF-pn5-view-xSR-gK-reL.nib
-rw-r--r--   1 mayoff  staff  2473 May 11 22:13 UINavigationController-ZKB-z3-xgf.nib
-rw-r--r--   1 mayoff  staff   847 May 11 22:13 UIPageViewController-ufv-JN-y6U.nib

Info.plist сопоставляет имена сцен в вашем раскадровке с соответствующими перьями:

:; plutil -p Info.plist 
{
  "UIViewControllerIdentifiersToNibNames" => {
    "AccountCollection" => "AccountCollection"
    "UINavigationController-ZKB-z3-xgf" => "UINavigationController-ZKB-z3-xgf"
    "UIPageViewController-ufv-JN-y6U" => "UIPageViewController-ufv-JN-y6U"
    "AccountEditor" => "AccountEditor"
  }
  "UIStoryboardDesignatedEntryPointIdentifier" => "UINavigationController-ZKB-z3-xgf"
  "UIStoryboardVersion" => 1
}

Сцена появляется только в этом списке, если у него есть идентификатор раскадровки, или к нему подключается segue, или это начальная сцена.

Список файлов nib в Info.plist do not содержит иерархии представлений этих контроллеров представлений. Каждый из этих файлов nib содержит контроллер представления его сцены и любые другие объекты верхнего уровня в сцене, но не вид контроллера представления или любые его подзоны.

Отдельный файл nib содержит иерархию представлений для сцены. Имя иерархии вида nib, полученное из идентификаторов объектов контроллера вида и его верхнего уровня. Вы можете увидеть идентификатор объекта любого объекта в раскадровке в "Identity Inspector" в Xcode. Например, мой идентификатор ID сцены сценария "AccountCollection" равен BYZ-38-t0r, а его идентификатор вида 8bC-Xf-vdC, поэтому иерархия представления для сцены находится в файле BYZ-38-t0r-view-8bC-Xf-vdC.nib. Файл nib файла сцены содержит имя файла nib файла иерархии представлений:

:; strings - AccountCollection.nib |grep -e '-.*-'
UIPageViewController-ufv-JN-y6U
BYZ-38-t0r-view-8bC-Xf-vdC          <---------
UpstreamPlaceholder-5Hn-fK-fqQ
UpstreamPlaceholder-8GL-mk-Rao
q1g-aL-SLo.title

Если сцена не имеет иерархии представлений, тогда будет только файл nib для контроллера представления и отдельный файл nib для иерархии представлений. Например, сценарий UIPageViewController не имеет иерархии представлений в раскадровке, поэтому нет иерархии представлений nib, соответствующей UIPageViewController-ufv-JN-y6U.nib.

Итак, что же все это связано с вашим вопросом? Вот что: когда ваше приложение загружает сцену из "раскадровки", она загружает файл nib, содержащий контроллер представления (и другие вершины -уровневые объекты). Когда загрузчик nib загружает этот файл nib, он отправляет awakeFromNib всем объектам, которые он только что загрузил. Это включает в себя ваш контроллер представления, но он не включает ваши представления, потому что ваши представления не были в этом файле nib.

Позже, когда вашему контроллеру просмотра будет предложено его свойство view, он загружает файл nib, содержащий его иерархию представлений. Контроллер представления передает себя в -[UINib instantiateWithOwner:options:] как аргумент owner. Так загрузчик nib может подключать объекты в иерархии представлений к выходам и действиям контроллера вида.

Когда загрузочный загрузчик nib загружает иерархию представлений, он отправляет awakeFromNib ко всем объектам, которые он только что загрузил. Поскольку ваш контроллер просмотра был не одним из этих объектов, ваш контроллер представления не получает сообщение awakeFromNib в настоящее время.

Когда возвращается instantiateWithOwner:options:, контроллер представления отправляет сообщение viewDidLoad. Это ваша возможность внести изменения в иерархию представлений.

Ответ 2

Контроллеры просмотра ждут, пока их просмотр будет доступен для фактического создания своего представления. Поскольку кнопка находится в представлении контроллера вида, она еще не будет создана.