@StateObject: چیست، ایجاد و مدیریت ObservableObject

نویسنده: IT Sectr منتشر شده: 2026-06-26 زمان مطالعه: 9 دقیقه

@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 برای ایجاد و مالکیت ObservableObject درون View.
  • یک بار ایجاد — شیء یک بار در طول عمر View مقداردهی می‌شود و در بازسازی‌ها دوباره ساخته نمی‌شود.
  • Source of truth — @StateObject برخلاف @ObservedObject منبع حقیقت در سلسله‌مراتب است.
  • چرخه حیات — شیء تا زمانی که View در حافظه وجود دارد زنده است و همراه با آن نابود می‌شود.
  • مقداردهی اولیه — @StateObject در زمان ایجاد نیاز به مقدار اولیه دارد، معمولاً از طریق init با پارامترها.

@StateObject در SwiftUI چیست

@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 والد ایجاد کرده و از طریق مقداردهنده اولیه ارسال کنند. این منجر به تکرار کد و خطر بازسازی تصادفی شیء می‌شد.

swift
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 چگونه کار می‌کند

مکانیزم @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

  • ایجاد — در اولین ظهور View روی صفحه، SwiftUI مقداردهنده اولیه شیء را فراخوانی کرده و ارجاع را ذخیره می‌کند.
  • بازسازی — هنگام به‌روزرسانی View والد، شیء دوباره ساخته نمی‌شود، از نمونه موجود استفاده می‌شود.
  • نابودی — وقتی View صفحه را ترک کرده و از سلسله‌مراتب حذف می‌شود، SwiftUI deinit شیء را فراخوانی می‌کند.

@StateObject در مقابل @ObservedObject: تفاوت‌های کلیدی

تفاوت اصلی @StateObject و @ObservedObject در این است که چه کسی مالک شیء است. @StateObject شیء را ایجاد کرده و ذخیره می‌کند — او مالک است. @ObservedObject فقط شیئی را مشاهده می‌کند که در جای دیگری ایجاد شده و از طریق مقداردهنده اولیه یا ویژگی ارسال شده است.

ویژگی@StateObject@ObservedObject
مالکیتایجاد کرده و مالک شیء استفقط مشاهده می‌کند
مقداردهی اولیهدرون View از طریق init/defaultاز خارج، از طریق پارامتر ارسال می‌شود
چرخه حیاتبه چرخه حیات View وابسته استتوسط View کنترل نمی‌شود
بازسازیدر به‌روزرسانی دوباره ساخته نمی‌شودمی‌تواند از خارج جایگزین شود
نسخه iOSiOS 14+iOS 13+

قانون ساده است: اگر View ایجاد می‌کند ObservableObject — از @StateObject استفاده کنید. اگر View فقط شیء آماده را از والد دریافت می‌کند — از @ObservedObject استفاده کنید. نقض این قانون یا به از دست رفتن داده‌ها (استفاده از @ObservedObject برای مالکیت) یا به ایجاد بیش از حد اشیاء (استفاده از @StateObject برای مشاهده) منجر می‌شود.

چه زمانی از @StateObject استفاده کنیم

@StateObject باید در Viewهایی استفاده شود که منبع حقیقت برای مجموعه داده مشخصی هستند. سناریوهای معمول شامل صفحه‌هایی با view model خاص خود، صفحه‌های ریشه پشته‌های ناوبری و ارائه‌های modal هستند که حالت خود را مدیریت می‌کنند.

  • صفحه با view model — هر صفحه‌ای که داده‌ها و منطق خود را مدیریت می‌کند باید view model خود را از طریق @StateObject ایجاد کند.
  • View ریشه — در سلسله‌مراتب NavigationStack یا TabView، عنصر ریشه داده‌ها را ایجاد کرده و فرزندان آنها را از طریق @ObservedObject دریافت می‌کنند.
  • پنجره‌های Modal — .sheet و .fullScreenCover اغلب برای مدیریت فرم یا فرآیند به @StateObject خاص خود نیاز دارند.
  • لیست با ویرایش — هر ردیف از لیست که حاوی فرم ویرایش است باید @StateObject خاص خود را داشته باشد.
swift
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 با پارامترها

مقداردهی اولیه @StateObject با پارامترها نیاز به نحو خاصی دارد، زیرا SwiftUI ایجاد شیء را به طور مستقل مدیریت می‌کند. نمی‌توان به سادگی پارامترها را به مقداردهنده اولیه ارسال کرد — باید از escaping closure یا یک متد ایجاد جداگانه استفاده کرد.

طبق Swift by Sundell (2024)، تمیزترین روش استفاده از یک متد کارخانه‌ای یا closure است که SwiftUI در اولین ایجاد شیء فراخوانی می‌کند. رویکرد جایگزین — مقداردهی اولیه ObservableObject در View والد و ارسال آن از طریق @StateObject با استفاده از مقداردهنده اولیه استاندارد.

swift
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ها در مقداردهنده‌های اولیه است.

اشتباهات رایج با @StateObject

رایج‌ترین اشتباه — استفاده از @ObservedObject به جای @StateObject برای View که باید مالک شیء باشد. در این حالت، در هر بازسازی والد، شیء دوباره ساخته می‌شود که منجر به از دست رفتن تمام داده‌های جمع‌آوری شده می‌شود. این اشتباه به خصوص در سلسله‌مراتب پیچیده با NavigationStack یا TabView موذیانه است.

  • از دست رفتن داده‌ها در ناوبری — اگر صفحه فرزند از @ObservedObject برای view model خود استفاده کند، پس از بازگشت و باز کردن مجدد، داده‌ها بازنشانی می‌شوند.
  • نشت حافظه — ایجاد @StateObject در View والدی که هرگز حذف نمی‌شود می‌تواند منجر به انباشت اشیاء شود، اگر هر صفحه فرزند نیز بدون کنترل @StateObject ایجاد کند.
  • تکرار اشیاء — ارسال یک ObservableObject به چندین @StateObject در Viewهای مختلف چندین نمونه مستقل ایجاد می‌کند که با یکدیگر همگام نیستند.

برای جلوگیری از این مشکلات، از قانون ساده پیروی کنید: یک @StateObject برای هر منبع حقیقت. اگر داده‌ها باید بین چندین صفحه مشترک باشند — @StateObject را یک بار در View ریشه ایجاد کرده و از طریق @ObservedObject یا @EnvironmentObject به عناصر فرزند ارسال کنید.

swift
// ❌ اشتباه: @ObservedObject برای مالکیت یک شیء
struct BadView: View {
    @ObservedObject var vm = ViewModel() // در هر به‌روزرسانی دوباره ساخته می‌شود!
}

// ✅ درست: @StateObject برای مالکیت
struct GoodView: View {
    @StateObject var vm = ViewModel() // یک بار برای طول عمر View ایجاد شد
}

سوالات متداول

تفاوت بین @StateObject و @State چیست؟

@State با نوع‌های مقداری (ساختارها، رشته‌ها، اعداد) کار می‌کند و مقدار را مستقیماً در حافظه SwiftUI ذخیره می‌کند. @StateObject با نوع‌های ارجاعی — کلاس‌های منطبق با ObservableObject — کار می‌کند. @State برای حالت‌های محلی ساده مناسب است، @StateObject برای اشیاء پیچیده با منطق و ویژگی‌های منتشرشده.

آیا می‌توان از @StateObject در iOS 13 استفاده کرد؟

خیر، @StateObject فقط از iOS 14 به بالا در دسترس است. برای iOS 13 از @ObservedObject استفاده کرده و ObservableObject را در View والد از طریق @State با مدیریت دستی چرخه حیات ایجاد کنید. جایگزین — استفاده از @State با struct به جای class برای داده‌هایی که به معناشناسی ارجاعی نیاز ندارند.

اگر از @StateObject در View فرزندی که شیء از والد دریافت می‌کند استفاده کنم چه اتفاقی می‌افتد؟

View فرزند کپی خود را از ObservableObject ایجاد می‌کند که کاملاً مستقل از والد است. تغییرات در یکی بر دیگری تأثیر نخواهد گذاشت. این تقریباً همیشه اشتباه است: برای دریافت شیء از والد از @ObservedObject و برای ایجاد شیء جدید درون View فقط از @StateObject استفاده کنید.

شیء ایجاد شده از طریق @StateObject چه زمانی نابود می‌شود؟

شیء زمانی نابود می‌شود که View ایجادکننده آن به طور کامل از سلسله‌مراتب SwiftUI حذف شود. برای صفحه در NavigationStack این اتفاق هنگام pop از پشته ناوبری می‌افتد. برای پنجره modal — هنگام بسته شدن. برای TabView — هنگام تغییر برگه، اگر View ذخیره نشود.

چگونه پارامترها را در زمان مقداردهی اولیه به @StateObject ارسال کنیم؟

از init سفارشی با دسترسی به property wrapper از طریق زیرخط استفاده کنید: _viewModel = StateObject(wrappedValue: MyViewModel(param: value)). این الگو امکان ارسال هر پارامتری را به ObservableObject فراهم می‌کند و در عین حال تضمین ایجاد یک‌باره شیء در طول عمر View را حفظ می‌کند.

خلاصه

  • @StateObject — property wrapper برای ایجاد و مالکیت ObservableObject درون View، قابل دسترس از iOS 14.
  • تضمین ایجاد یک‌باره — شیء یک بار مقداردهی می‌شود و در بازسازی View دوباره ساخته نمی‌شود.
  • Source of truth — @StateObject منبع حقیقت است و @ObservedObject فقط مشاهده‌کننده.
  • چرخه حیات — شیء تا زمانی که View در سلسله‌مراتب SwiftUI وجود دارد زنده است و هنگام ترک آن نابود می‌شود.
  • مقداردهی اولیه با پارامترها — نیاز به دسترسی به property wrapper از طریق _viewModel و StateObject(wrappedValue:).
  • اشتباه مالکیت — استفاده از @ObservedObject برای ایجاد شیء منجر به از دست رفتن داده‌ها در بازسازی می‌شود.
  • یک شیء — یک @StateObject — برای داده‌های مشترک @StateObject را در View ریشه ایجاد کرده و از طریق @ObservedObject به فرزندان ارسال کنید.

ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد

IT Sectr از سال 2017 برنامه‌های iOS و Android را برای استارتاپ‌ها و کسب‌وکارها ایجاد می‌کند. ما به شما مشاوره می‌دهیم و بهترین راه‌حل را پیشنهاد خواهیم کرد.

بحث درباره پروژه

همچنین بخوانید