Узнайте, когда SwiftUI перерисовывает подпредставления
Одним из удивительных аспектов профессии программиста является то, что вы никогда не прекращаете учиться. Вы каждый день сталкиваетесь с различными технологиями: изучение их основных концепций — одна из вещей, которые лично мне нравятся больше всего.
Одним из наиболее важных навыков для изучения новых технологий является способность распознавать закономерности и сходства. Это позволяет вам рассуждать о новой технологии, даже если вы не используете ее каждый день.
Неделю назад мой коллега прислал мне эту статью по рендерингу в React. Я не использую React каждый день, но я знаком с его основными понятиями и не мог заметить, насколько он похож на SwiftUI. Читая статью, я был удивлен, увидев, что React выполняет повторную визуализацию при изменении состояния компонента, а не при изменении его реквизитов.
Итак, мое любопытство защипало: что делает SwiftUI? Когда он перерисовывает свои компоненты? Я создал небольшой проект, чтобы попытаться понять это, и сегодня я хочу поделиться своим процессом и своими выводами.
Воссоздание приложения
Первым шагом, который я предпринял, было воспроизвести ту же ситуацию, что и в статье. Я создал новый проект SwiftUI с теми же исходными компонентами:
BigCountNumber
Counter
Для простоты я поместил их в один файл:
Затем я изменил RenderingApp.swift
, чтобы загрузить представление Counter
вместо ContentView
, чтобы я мог его протестировать.
Запустив приложение в симуляторе, я мог видеть, как счетчик увеличивается каждый раз, когда я нажимал на кнопку.
Визуализируйте визуализацию
Второй шаг — понять, как визуализировать перерисовку компонента. Я обнаружил функцию static_printDebug()
протокола View
SwiftUI: закрытый метод, который можно использовать для отладки View
SwiftUI при его рендеринге.
Внимание! не забудьте удалить вызов этой функции перед отправкой в Apple. В противном случае ваше приложение будет автоматически отклонено: вы не можете использовать частные системные API в своем приложении.
При введении вызова функции в тело нашего View
мы также должны добавить обратно оператор return
: SwiftUI не может сделать вывод, что вы хотите вернуть, если тело свойства длиннее одного оператора.
Новый код выглядит так:
Запустив приложение сейчас, Xcode напечатал несколько сообщений в консоли. При запуске приложения Xcode печатает:
И, после нажатия на кнопку increment
несколько раз, печатается:
Добавление украшения
React и SwiftUI в настоящее время ведут себя одинаково. В этой настройке изменение State
аналогично изменению свойств задействованных компонентов.
Давайте добавим компонент Decoration
, как это предлагается в справочной статье.
Чтобы отслеживать, когда отображается Decoration
, я добавил код в _printChanges
. Составим его с компонентом Counter
, как показано в статье:
Запустив приложение, Xcode выводит следующий вывод.
Мы можем заметить, что Decoration
появляется только один раз, в то время как Counter
и BigCountNumber
обновляются каждый раз, когда мы нажимаем на кнопку.
По умолчанию SwiftUI ведет себя более консервативно, чем React: он избегает ненужного рендеринга компонентов, чьи реквизиты не меняются.
Почему?
Мое лучшее объяснение касается семантики типов компонентов. В SwiftUI мы создаем View
s, которые являются struct
: по умолчанию неизменяемыми типами. Это значительно упрощает проверку, когда что-то меняется, а когда нет.
Если свойства View
меняются, мы должны воссоздать его: в body
View
мы используем инициализаторы различных компонентов, которые создают View
. По сути, мы говорим среде выполнения: «Смотрите, я создаю новое представление. Можешь нарисовать?».
React, с другой стороны, работает со ссылочными типами: компонентами класса или функции. Гораздо сложнее (и дороже) понять, когда что-то меняется: компоненты не создаются постоянно. React должен решить, когда снова вызывать функцию render
(или функцию, которая определяет сам компонент). Когда переменная class
изменяется — например, одна из его реквизитов — адрес памяти класса не меняется.
React обещает предоставить самую обновленную версию пользовательского интерфейса. Для этого ему приходится рендерить свои компоненты чаще, чем SwiftUI: каждый раз при изменении состояния.
Заключение
В сегодняшней статье мы рассмотрели различия в рендеринге между React и SwiftUI. Мы узнали, как использовать приватную функцию _printChanges
для отладки нашего процесса рендеринга, и создали экспериментальный проект, чтобы протестировать его.
Мы узнали, что SwiftUI перерисовывает свой компонент при изменении их свойств, а не при изменении состояния, как это делает React. Это возможно главным образом для значения семантики структур.