@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 که با نوعهای مقداری (ساختارها) کار میکند، @StateObject برای نوعهای ارجاعی — کلاسهایی که میتوانند SwiftUI را از تغییرات ویژگیهای خود مطلع کنند — طراحی شده است.
وقتی View از @StateObject var viewModel: MyViewModel استفاده میکند، SwiftUI به طور خودکار نمونه MyViewModel را در اولین نمایش View ایجاد کرده و آن را در حافظه مخصوص فریمورک ذخیره میکند. در هر بهروزرسانی View (مثلاً هنگام تغییر حالت والد)، 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("تعداد: \(viewModel.count)")
Button("افزایش", action: viewModel.increment)
}
}
}
مکانیزم @StateObject بر پایه یکپارچگی SwiftUI با فریمورک Combine است. وقتی ObservableObject ویژگیهای خود را با ویژگی @Published علامتگذاری میکند، SwiftUI به طور خودکار تغییرات را از طریق publisher تعبیهشده در پروتکل ObservableObject دنبال میکند. هنگام تغییر یک ویژگی منتشرشده، شیء سیگنالی از طریق publisher objectWillChange ارسال میکند که بازترسیم همه Viewهای مشاهدهکننده آن شیء را فعال میکند.
SwiftUI نمونه ObservableObject را در حافظه مخصوصی که به نمونه خاص View متصل است ذخیره میکند. این حافظه یک بار در اولین رندر ایجاد شده و تا زمان نابودی View وجود دارد. به همین دلیل @StateObject پایداری ارجاع به شیء را تضمین میکند — SwiftUI حافظه را به طور خودکار مدیریت میکند و به مقداردهنده اولیه View وابسته نیست.
طبق objc.io — Thinking in SwiftUI (2025)، پیادهسازی داخلی @StateObject از مکانیزمی مشابه @State اما برای نوعهای ارجاعی استفاده میکند: 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 خاص خود، صفحههای ریشه پشتههای ناوبری و ارائههای modal هستند که حالت خود را مدیریت میکنند.
struct ProfileView: View {
@StateObject var viewModel = ProfileViewModel()
var body: some View {
NavigationStack {
Form {
TextField("Name", text: $viewModel.name)
TextField("Email", text: $viewModel.email)
Button("ذخیره") {
viewModel.saveProfile()
}
}
.navigationTitle("Profile")
}
}
}
مقداردهی اولیه @StateObject با پارامترها نیاز به نحو خاصی دارد، زیرا SwiftUI ایجاد شیء را به طور مستقل مدیریت میکند. نمیتوان به سادگی پارامترها را به مقداردهنده اولیه ارسال کرد — باید از escaping closure یا یک متد ایجاد جداگانه استفاده کرد.
طبق Swift by Sundell (2024)، تمیزترین روش استفاده از یک متد کارخانهای یا closure است که 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 wrapperها در مقداردهندههای اولیه است.
رایجترین اشتباه — استفاده از @ObservedObject به جای @StateObject برای View که باید مالک شیء باشد. در این حالت، در هر بازسازی والد، شیء دوباره ساخته میشود که منجر به از دست رفتن تمام دادههای جمعآوری شده میشود. این اشتباه به خصوص در سلسلهمراتب پیچیده با NavigationStack یا TabView موذیانه است.
برای جلوگیری از این مشکلات، از قانون ساده پیروی کنید: یک @StateObject برای هر منبع حقیقت. اگر دادهها باید بین چندین صفحه مشترک باشند — @StateObject را یک بار در View ریشه ایجاد کرده و از طریق @ObservedObject یا @EnvironmentObject به عناصر فرزند ارسال کنید.
// ❌ اشتباه: @ObservedObject برای مالکیت یک شیء
struct BadView: View {
@ObservedObject var vm = ViewModel() // در هر بهروزرسانی دوباره ساخته میشود!
}
// ✅ درست: @StateObject برای مالکیت
struct GoodView: View {
@StateObject var vm = ViewModel() // یک بار برای طول عمر View ایجاد شد
}
سوالات متداول
@State با نوعهای مقداری (ساختارها، رشتهها، اعداد) کار میکند و مقدار را مستقیماً در حافظه SwiftUI ذخیره میکند. @StateObject با نوعهای ارجاعی — کلاسهای منطبق با ObservableObject — کار میکند. @State برای حالتهای محلی ساده مناسب است، @StateObject برای اشیاء پیچیده با منطق و ویژگیهای منتشرشده.
خیر، @StateObject فقط از iOS 14 به بالا در دسترس است. برای iOS 13 از @ObservedObject استفاده کرده و ObservableObject را در View والد از طریق @State با مدیریت دستی چرخه حیات ایجاد کنید. جایگزین — استفاده از @State با struct به جای class برای دادههایی که به معناشناسی ارجاعی نیاز ندارند.
View فرزند کپی خود را از ObservableObject ایجاد میکند که کاملاً مستقل از والد است. تغییرات در یکی بر دیگری تأثیر نخواهد گذاشت. این تقریباً همیشه اشتباه است: برای دریافت شیء از والد از @ObservedObject و برای ایجاد شیء جدید درون View فقط از @StateObject استفاده کنید.
شیء زمانی نابود میشود که View ایجادکننده آن به طور کامل از سلسلهمراتب SwiftUI حذف شود. برای صفحه در NavigationStack این اتفاق هنگام pop از پشته ناوبری میافتد. برای پنجره modal — هنگام بسته شدن. برای TabView — هنگام تغییر برگه، اگر View ذخیره نشود.
از init سفارشی با دسترسی به property wrapper از طریق زیرخط استفاده کنید: _viewModel = StateObject(wrappedValue: MyViewModel(param: value)). این الگو امکان ارسال هر پارامتری را به ObservableObject فراهم میکند و در عین حال تضمین ایجاد یکباره شیء در طول عمر View را حفظ میکند.
خلاصه
ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد
IT Sectr از سال 2017 برنامههای iOS و Android را برای استارتاپها و کسبوکارها ایجاد میکند. ما به شما مشاوره میدهیم و بهترین راهحل را پیشنهاد خواهیم کرد.
همچنین بخوانید