@StateObject — это property wrapper в SwiftUI, который создаёт и владеет экземпляром ObservableObject на всём протяжении жизненного цикла View. Когда View впервые появляется на экране, @StateObject инициализирует объект и хранит его до тех пор, пока View не будет удалено из памяти. Это гарантирует, что данные не сбросятся при перестроении интерфейса — например, при смене темы или обновлении родительского View. По данным Apple Developer Documentation (2025), @StateObject должен использоваться как основной источник истины (source of truth) для ObservableObject в иерархии SwiftUI, в то время как дочерние View получают уже созданный объект через @ObservedObject или @EnvironmentObject.
Главное
@StateObject — это property wrapper, представленный в iOS 14, который позволяет View создавать и владеть экземпляром класса, соответствующего протоколу ObservableObject. В отличие от @State, который работает с value-типами (структурами), @StateObject предназначен для reference-типов — классов, которые могут уведомлять SwiftUI об изменениях своих свойств.
Когда View использует @StateObject var viewModel: MyViewModel, SwiftUI автоматически создаёт экземпляр MyViewModel при первом отображении View и сохраняет его в специальном хранилище фреймворка. При каждом обновлении View (например, при изменении родительского state) SwiftUI не пересоздаёт объект — он использует существующий экземпляр до тех пор, пока View не будет удалено из иерархии.
По данным Apple WWDC Session 10137 (2024), @StateObject решает проблему потери данных при перестроении View, которая существовала в iOS 13, когда разработчикам приходилось создавать ObservableObject в родительском View и передавать его через инициализатор. Это приводило к дублированию кода и риску случайного пересоздания объекта.
import SwiftUI
class CounterViewModel: ObservableObject {
@Published var count: Int = 0
func increment() {
count += 1
}
}
struct CounterView: View {
@StateObject var viewModel = CounterViewModel()
var body: some View {
VStack {
Text("Count: \(viewModel.count)")
Button("Increment", action: viewModel.increment)
}
}
}
Механизм @StateObject основан на интеграции SwiftUI с Combine framework. Когда ObservableObject помечает свои свойства атрибутом @Published, SwiftUI автоматически подписывается на изменения через publisher, встроенный в протокол ObservableObject. При изменении published-свойства объект отправляет signal через objectWillChange publisher, что триггерит перерисовку всех View, наблюдающих за этим объектом.
SwiftUI хранит экземпляр ObservableObject в специальном storage, привязанном к конкретному экземпляру View. Этот storage создаётся один раз при первом рендеринге и существует до уничтожения View. Именно поэтому @StateObject гарантирует стабильность ссылки на объект — SwiftUI управляет памятью автоматически, не полагаясь на инициализатор View.
По данным objc.io — Thinking in SwiftUI (2025), внутренняя реализация @StateObject использует механизм, схожий с @State, но для reference-типов: SwiftUI создаёт boxing-обёртку вокруг объекта и управляет её жизненным циклом через собственный аллокатор, оптимизированный для частых перестроений иерархии View.
Главное отличие @StateObject от @ObservedObject заключается в том, кто владеет объектом. @StateObject создаёт и хранит объект — он является владельцем. @ObservedObject только наблюдает за объектом, который был создан где-то в другом месте и передан через инициализатор или свойство.
| Характеристика | @StateObject | @ObservedObject |
|---|---|---|
| Владение | Создаёт и владеет объектом | Только наблюдает |
| Инициализация | Внутри View через init/default | Снаружи, передаётся через параметр |
| Жизненный цикл | Привязан к жизненному циклу View | Не контролируется View |
| Пересоздание | Не пересоздаётся при обновлении | Может быть заменён извне |
| iOS версия | iOS 14+ | iOS 13+ |
Правило простое: если View создаёт ObservableObject — используй @StateObject. Если View только получает уже готовый объект от родителя — используй @ObservedObject. Нарушение этого правила приводит либо к потере данных (если использовать @ObservedObject для владения), либо к избыточному созданию объектов (если использовать @StateObject для наблюдения).
@StateObject следует использовать в тех View, которые являются источником истины для определённого набора данных. Типичные сценарии включают экраны с собственным view model, корневые экраны навигационных стеков и модальные представления, управляющие собственным состоянием.
struct ProfileView: View {
@StateObject var viewModel = ProfileViewModel()
var body: some View {
NavigationStack {
Form {
TextField("Name", text: $viewModel.name)
TextField("Email", text: $viewModel.email)
Button("Save") {
viewModel.saveProfile()
}
}
.navigationTitle("Profile")
}
}
}
Инициализация @StateObject с параметрами требует использования специального синтаксиса, поскольку SwiftUI управляет созданием объекта самостоятельно. Нельзя просто передать параметры в инициализатор — нужно использовать escaping замыкание или отдельный метод создания.
По данным Swift by Sundell (2024), наиболее чистый способ — использовать фабричный метод или замыкание, которое SwiftUI вызовет при первом создании объекта. Альтернативный подход — инициализировать ObservableObject в родительском View и передать его через @StateObject с использованием стандартного инициализатора.
class UserViewModel: ObservableObject {
@Published var user: User
init(user: User) {
self.user = user
}
}
struct UserDetailView: View {
@StateObject var viewModel: UserViewModel
init(user: User) {
_viewModel = StateObject(wrappedValue: UserViewModel(user: user))
}
var body: some View {
Text(viewModel.user.name)
}
}
Важно помнить, что инициализатор View с @StateObject должен использовать подчёркивание перед именем свойства (_viewModel) для доступа к самому property wrapper, а не к его значению. Это стандартный паттерн Swift для работы с property wrappers в инициализаторах.
Наиболее распространённая ошибка — использование @ObservedObject вместо @StateObject для View, которое должно владеть объектом. В этом случае при каждом перестроении родителя объект будет создаваться заново, что приведёт к потере всех накопленных данных. Эта ошибка особенно коварна в сложных иерархиях с NavigationStack или TabView.
Чтобы избежать этих проблем, следуй простому правилу: один @StateObject на один источник истины. Если данные должны быть общими для нескольких экранов — создай @StateObject один раз в корневом View и передавай через @ObservedObject или @EnvironmentObject дочерним элементам.
// ❌ Wrong: @ObservedObject for owning an object
struct BadView: View {
@ObservedObject var vm = ViewModel() // will be recreated on each update!
}
// ✅ Correct: @StateObject for owning
struct GoodView: View {
@StateObject var vm = ViewModel() // created once for View lifetime
}
Часто задаваемые вопросы
@State работает с value-типами (структуры, строки, числа) и хранит значение непосредственно в SwiftUI storage. @StateObject работает с reference-типами — классами, соответствующими ObservableObject. @State подходит для простых локальных состояний, @StateObject — для сложных объектов с логикой и published-свойствами.
Нет, @StateObject доступен только с iOS 14 и выше. Для iOS 13 используйте @ObservedObject и создавайте ObservableObject в родительском View через @State с ручным управлением жизненным циклом. Альтернатива — использовать @State с struct вместо class для данных, которые не требуют reference semantics.
Дочерний View создаст свою собственную копию ObservableObject, полностью независимую от родительской. Изменения в одном не отразятся на другом. Это почти всегда ошибка: используйте @ObservedObject для получения объекта из родителя и @StateObject только для создания нового объекта внутри View.
Объект уничтожается, когда View, которое его создало, полностью удаляется из иерархии SwiftUI. Для экрана в NavigationStack это происходит при pop с навигационного стека. Для модального окна — при его закрытии. Для TabView — при переключении вкладки, если View не кешируется.
Используй кастомный init с доступом к property wrapper через подчёркивание: _viewModel = StateObject(wrappedValue: MyViewModel(param: value)). Этот паттерн позволяет передать любые параметры в ObservableObject, сохраняя при этом гарантию единственного создания объекта за время жизни View.
Итоги
Мы разработаем мобильное приложение под ключ
IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.
Читайте также