Ответ
XIB (XML Interface Builder) компилируется в бинарный NIB-файл на этапе сборки проекта. Непосредственное чтение (десериализация) NIB и создание объектов интерфейса происходит во время выполнения (runtime) при явном вызове методов загрузки.
Типичные точки загрузки:
- Для UIViewController: При инициализации через
init(nibName:bundle:)или при создании контроллера из storyboard, если для него указан отдельный XIB. - Для UIView: При явном вызове
UINib(nibName:bundle:).instantiate(withOwner:options:)илиBundle.main.loadNibNamed(_:owner:options:).
Пример:
// Загрузка кастомного UIView из XIB
if let loadedViews = Bundle.main.loadNibNamed("CustomView", owner: nil, options: nil),
let customView = loadedViews.first as? CustomView {
addSubview(customView)
}
// Кэширование UINib для эффективной многократной загрузки
static let nib = UINib(nibName: "CustomCell", bundle: nil)
let cell = Self.nib.instantiate(withOwner: nil, options: nil).first as! CustomCell
Важные детали:
- Загрузка происходит синхронно в том потоке, где вызвана (обычно главный).
- После создания объектов из NIB автоматически устанавливаются связи IBOutlet и IBAction.
- Для оптимизации интерфейсов, создаваемых многократно (например, ячеек таблицы), рекомендуется кэшировать экземпляр
UINib.
Ответ 18+ 🔞
А, слушай, про эту вашу XIB-магию. Ну, типа, пока ты там в Xcode кнопочки тыкаешь, это всё просто XML-файл, понятно? А потом, когда проект собирается, его нахуй компилируют в бинарный NIB — это типа сжатый, оптимизированный формат, чтоб в рантайме быстрее грузился.
А вот самое интересное, блядь, происходит уже во время работы приложения, когда эти NIB'ы начинают выгружать из памяти и превращать в реальные объекты. Это не магия, это просто код вызывают.
Где обычно этот цирк начинается:
- Для UIViewController: Когда ты его создаёшь через
init(nibName:bundle:). Или если в сториборде у контроллера стоит галочка «отдельный XIB файл» — система сама, тихо, как мышь, под капотом вызовет этот инит. - Для UIView: Когда ты, мучаясь, пишешь
Bundle.main.loadNibNamed(...)или создаёшьUINib. Вот тут ты уже сам, руками, всё инициируешь.
Смотри, как это выглядит в коде, чтоб не пиздеть:
// Допустим, тебе надо кастомную вьюху из XIB'а достать. Делаешь так:
if let loadedViews = Bundle.main.loadNibNamed("CustomView", owner: nil, options: nil),
let customView = loadedViews.first as? CustomView {
addSubview(customView) // И вот она, родимая, на экране
}
// А вот лайфхак, чтобы не грузить одно и то же по сто раз (например, для ячеек таблицы):
static let nib = UINib(nibName: "CustomCell", bundle: nil) // Создал один раз — кэшировал
let cell = Self.nib.instantiate(withOwner: nil, options: nil).first as! CustomCell // Используешь где надо
А теперь, блядь, важные нюансы, которые в жопу ткнут, если про них забыть:
- Загрузка идёт синхронно, в том же потоке, откуда вызвали. Если на главном — то всё норм. Если на бэкграунде — готовься к крешам, ёпта.
- Система сама, автоматом, расставит все IBOutlet и IBAction связи, которые ты накрутил в Interface Builder'е. Это удобно, да.
- И да, повторюсь, как мантру: если что-то грузишь чаще, чем раз в пять минут (ячейки, вьюхи в коллекции), кэшируй
UINib, не будь рохлей. Экономия ресурсов — наше всё, ебать мои старые костыли.