В чём разница между @State, @Binding, @ObservedObject и @StateObject?
@State хранит локальное состояние, @Binding — ссылка на чужое состояние, @StateObject создаёт и владеет ObservableObject, @ObservedObject только наблюдает за переданным снаружи объектом.
@State
@State — локальное состояние, принадлежащее конкретному представлению. SwiftUI хранит значение за пределами структуры, поэтому оно сохраняется при ре-рендерах. Используйте @State для простых значений (числа, строки, булевы), которые не нужны другим представлениям.
struct CounterView: View {
@State private var count = 0 // приватное, только для этого вью
var body: some View {
VStack {
Text("\(count)")
Button("+") { count += 1 }
}
}
}
@Binding
@Binding — ссылка на состояние, принадлежащее другому представлению. Дочернее представление может читать и изменять значение, но не владеет им. Создаётся из @State с помощью префикса $.
struct ToggleRow: View {
@Binding var isEnabled: Bool // владеет родитель
var body: some View {
Toggle("Активно", isOn: $isEnabled)
}
}
struct SettingsView: View {
@State private var notificationsOn = true
var body: some View {
ToggleRow(isEnabled: $notificationsOn) // передаём binding
}
}
@StateObject
@StateObject создаёт экземпляр ObservableObject и владеет им. SwiftUI гарантирует, что объект будет создан один раз и уничтожен вместе с представлением. Используйте @StateObject, когда представление является владельцем модели.
final class FormViewModel: ObservableObject {
@Published var name = ""
@Published var email = ""
}
struct FormView: View {
@StateObject private var vm = FormViewModel() // создаёт и владеет
var body: some View {
Form {
TextField("Имя", text: $vm.name)
TextField("Email", text: $vm.email)
}
}
}
@ObservedObject
@ObservedObject подписывается на уже существующий ObservableObject, но не владеет им. Объект должен быть создан где-то выше и передан извне. Если представление с @ObservedObject пересоздаётся, объект может быть уничтожен и пересоздан — это опасная ловушка.
struct DetailView: View {
@ObservedObject var vm: FormViewModel // не создаёт, только наблюдает
var body: some View {
Text(vm.name)
}
}
struct RootView: View {
@StateObject private var vm = FormViewModel() // здесь создаётся
var body: some View {
DetailView(vm: vm) // передаём вниз
}
}
Когда что использовать
@State— простые локальные значения (Bool, Int, String).@Binding— значение из родительского представления, которое нужно изменять.@StateObject— когда текущее представление создаёт и владеет ViewModel (ObservableObject).@ObservedObject— когда ViewModel создана выше и передаётся в дочернее представление.
Подводные камни
- Использование
@ObservedObjectвместо@StateObjectдля создания объекта — объект будет пересоздаваться при каждом ре-рендере родителя, сбрасывая состояние. @Stateработает только внутри представления — попытка читать его значение в init до появления body не даёт ожидаемого результата.- Передача
@State-переменной без$создаёт копию значения, а не Binding — изменения не будут отражены в родителе. - В iOS 17+ для
@Observable-классов не нужны@StateObject/@ObservedObject— используйте просто@Stateдля владения и передавайте напрямую. - Объект в
@StateObjectинициализируется лениво, но аргументы инициализатора вычисляются при каждом ре-рендере — не передавайте туда тяжёлые вычисления. @Bindingнельзя создать из произвольного значения без@Stateили константойBinding.constant(value)— последнее подходит только для Preview.
Common mistakes
- Сводить «
@State,@Binding,@ObservedObjectи@StateObject» к синтаксису и не объяснять navigation state. - Игнорировать жизненный цикл, основной поток или момент освобождения ресурсов в сценарии swiftui-2.
- Выбирать API по привычке, не проверяя состояние, ошибки, доступность и платформенные ограничения.
What the interviewer is testing
- Формулирует точную модель для «
@State,@Binding,@ObservedObjectи@StateObject» и подтверждает ее корректным примером. - Умеет связать ответ с environment, тестированием и отладкой на устройстве.
- Называет ограничения подхода swiftui-2, включая производительность, память и сопровождение.