@StateObject: ما هو، إنشاء وإدارة ObservableObject

المؤلف: IT Sectr نُشر: 2026-06-26 وقت القراءة: 9 دق

@StateObject هو property wrapper في SwiftUI يقوم بإنشاء وامتلاك مثيل ObservableObject طوال دورة حياة View بأكملها. عندما تظهر View لأول مرة على الشاشة، يقوم @StateObject بتهيئة الكائن وتخزينه حتى يتم إزالة View من الذاكرة. هذا يضمن عدم إعادة تعيين البيانات عند إعادة بناء الواجهة — على سبيل المثال، عند تغيير السمة أو تحديث View الأصل. وفقاً لوثائق مطوري Apple (2025)، يجب استخدام @StateObject كمصدر رئيسي للحقيقة (source of truth) لـ ObservableObject في تسلسل SwiftUI، بينما تتلقى Views الفرعية الكائن المنشأ بالفعل عبر @ObservedObject أو @EnvironmentObject.

الخلاصة

  • @StateObject — property wrapper لإنشاء وامتلاك ObservableObject داخل View.
  • إنشاء واحد — يتم تهيئة الكائن مرة واحدة خلال عمر View ولا يتم إعادة إنشائه عند إعادة البناء.
  • مصدر الحقيقة — @StateObject هو مصدر الحقيقة في التسلسل الهرمي، على عكس @ObservedObject.
  • دورة الحياة — يعيش الكائن طالما أن View موجودة في الذاكرة ويتم تدميره معها.
  • التهيئة — يتطلب @StateObject قيمة أولية عند الإنشاء، عادة عبر init مع معاملات.

ما هو @StateObject في SwiftUI

@StateObject هو property wrapper تم تقديمه في iOS 14 يسمح لـ View بإنشاء وامتلاك مثيل لفئة تتوافق مع بروتوكول ObservableObject. على عكس @State الذي يعمل مع أنواع القيم (structs)، فإن @StateObject مصمم لأنواع المرجع — الفئات التي يمكنها إخطار SwiftUI بالتغييرات في خصائصها.

عندما تستخدم View @StateObject var viewModel: MyViewModel، يقوم SwiftUI تلقائياً بإنشاء مثيل MyViewModel عند أول ظهور للـ View ويخزنه في مخزن خاص بالإطار. عند كل تحديث للـ View (على سبيل المثال، عندما تتغير حالة الأصل)، لا يقوم SwiftUI بإعادة إنشاء الكائن — بل يستخدم المثيل الحالي حتى تتم إزالة View من التسلسل الهرمي.

وفقاً لـ Apple WWDC Session 10137 (2024)، يحل @StateObject مشكلة فقدان البيانات التي كانت موجودة في iOS 13 عند إعادة بناء الـ Views، مما أجبر المطورين على إنشاء 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("Count: \(viewModel.count)")
            Button("Increment", action: viewModel.increment)
        }
    }
}

كيف يعمل @StateObject

آلية @StateObject تعتمد على تكامل SwiftUI مع إطار Combine. عندما يضع ObservableObject علامة على خصائصه بالسمة @Published، يشترك SwiftUI تلقائياً في التغييرات عبر الناشر المدمج في بروتوكول ObservableObject. عندما تتغير خاصية منشورة، يرسل الكائن إشارة عبر ناشر objectWillChange، مما يؤدي إلى إعادة رسم جميع الـ Views التي تراقب هذا الكائن.

يخزن SwiftUI مثيل ObservableObject في مخزن خاص مرتبط بمثيل View معين. يتم إنشاء هذا المخزن مرة واحدة أثناء أول عرض ويوجد حتى يتم تدمير الـ View. لهذا السبب يضمن @StateObject استقرار المرجع — يدير SwiftUI الذاكرة تلقائياً، دون الاعتماد على مُهيئ الـ View.

وفقاً لـ objc.io — Thinking in SwiftUI (2025)، يستخدم التنفيذ الداخلي لـ @StateObject آلية مشابهة لـ @State ولكن لأنواع المرجع: يقوم SwiftUI بإنشاء غلاف (boxing) حول الكائن ويدير دورة حياته من خلال مخصص الذاكرة الخاص به، المُحسّن لإعادة بناء التسلسل الهرمي للـ Views بشكل متكرر.

دورة حياة @StateObject

  • الإنشاء — عند أول ظهور للـ View على الشاشة، يستدعي SwiftUI مُهيئ الكائن ويخزن المرجع.
  • إعادة البناء — عند تحديث View الأصل، لا يتم إعادة إنشاء الكائن؛ بل يُستخدم المثيل الحالي.
  • التدمير — عندما تغادر الـ View الشاشة وتتم إزالتها من التسلسل الهرمي، يستدعي SwiftUI أداة التدمير (deinit) الخاصة بالكائن.

@StateObject مقابل @ObservedObject: الفروق الرئيسية

الفرق الرئيسي بين @StateObject و @ObservedObject يكمن في من يملك الكائن. @StateObject ينشئ ويخزن الكائن — هو المالك. @ObservedObject يراقب فقط الكائن الذي تم إنشاؤه في مكان آخر وتم تمريره عبر المُهيئ أو الخاصية.

الخاصية@StateObject@ObservedObject
الملكيةينشئ ويمتلك الكائنيراقب فقط
التهيئةداخل الـ View عبر init/افتراضيخارجية، تُمرر عبر معامل
دورة الحياةمربوطة بدورة حياة الـ Viewلا تتحكم بها الـ View
إعادة الإنشاءلا يُعاد إنشاؤه عند التحديثيمكن استبداله خارجياً
إصدار iOSiOS 14+iOS 13+

القاعدة بسيطة: إذا كانت الـ View تنشئ ObservableObject — استخدم @StateObject. إذا كانت الـ View فقط تستلم كائنًا منشأً مسبقاً من الأصل — استخدم @ObservedObject. مخالفة هذه القاعدة تؤدي إما إلى فقدان البيانات (إذا تم استخدام @ObservedObject للملكية) أو إلى إنشاء مفرط للكائنات (إذا تم استخدام @StateObject للمراقبة).

متى نستخدم @StateObject

يجب استخدام @StateObject في الـ Views التي تمثل مصدر الحقيقة لمجموعة محددة من البيانات. تشمل السيناريوهات النموذجية الشاشات التي تحتوي على view model خاص بها، والشاشات الجذرية لأكوام التنقل، والعروض التقديمية المشروطة التي تدير حالتها الخاصة.

  • شاشة مع view model — كل شاشة تدير بياناتها ومنطقها الخاص يجب أن تنشئ view model الخاص بها عبر @StateObject.
  • View جذرية — في تسلسل NavigationStack أو TabView، العنصر الجذري ينشئ البيانات، والعناصر الفرعية تستقبلها عبر @ObservedObject.
  • النوافذ المشروطة — .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("Save") {
                    viewModel.saveProfile()
                }
            }
            .navigationTitle("Profile")
        }
    }
}

تهيئة @StateObject مع معاملات

تهيئة @StateObject مع معاملات تتطلب صيغة خاصة، حيث أن SwiftUI يدير إنشاء الكائن بنفسه. لا يمكنك ببساطة تمرير معاملات إلى المُهيئ — بل تحتاج إلى استخدام إغلاق هارب (escaping closure) أو طريقة مصنع منفصلة.

وفقاً لـ Swift by Sundell (2024)، أنظف طريقة هي استخدام طريقة مصنع أو إغلاق سيقوم 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 wrappers في المُهيئات.

الأخطاء الشائعة مع @StateObject

الخطأ الأكثر شيوعاً هو استخدام @ObservedObject بدلاً من @StateObject لـ View التي يجب أن تمتلك الكائن. في هذه الحالة، في كل مرة يُعاد بناء الأصل، سيتم إعادة إنشاء الكائن، مما يؤدي إلى فقدان جميع البيانات المتراكمة. هذا الخطأ خطير بشكل خاص في التسلسلات الهرمية المعقدة مع NavigationStack أو TabView.

  • فقدان البيانات أثناء التنقل — إذا كانت شاشة فرعية تستخدم @ObservedObject لـ view model الخاص بها، فسيتم إعادة تعيين البيانات عند العودة وإعادة الفتح.
  • تسرب الذاكرة — إنشاء @StateObject في View رئيسية لا تتم إزالتها أبداً يمكن أن يؤدي إلى تراكم الكائنات إذا كانت كل شاشة فرعية تنشئ أيضاً @StateObject دون تحكم.
  • تكرار الكائنات — تمرير ObservableObject واحد إلى عدة @StateObject في Views مختلفة ينشئ عدة مثيلات مستقلة لا تتزامن مع بعضها البعض.

لتجنب هذه المشكلات، اتبع قاعدة بسيطة: @StateObject واحد لكل مصدر حقيقة. إذا كان يجب مشاركة البيانات عبر عدة شاشات — أنشئ @StateObject مرة واحدة في View الجذر ومرره عبر @ObservedObject أو @EnvironmentObject إلى العناصر الفرعية.

swift
// ❌ 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
}

الأسئلة الشائعة

ما الفرق بين @StateObject و @State؟

@State يعمل مع أنواع القيم (structs، سلاسل نصية، أرقام) ويخزن القيمة مباشرة في مخزن 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 لاستقبال كائن من الأصل و @StateObject فقط لإنشاء كائن جديد داخل الـ View.

متى يتم تدمير الكائن المنشأ عبر @StateObject؟

يتم تدمير الكائن عندما يتم إزالة الـ View التي أنشأته بالكامل من تسلسل SwiftUI. لشاشة في NavigationStack، يحدث هذا عند الخروج من مكدس التنقل. لنافذة مشروطة — عند إغلاقها. لـ TabView — عند تبديل التبويب، إذا لم تكن الـ View مخزنة مؤقتاً.

كيف تمرر معاملات إلى @StateObject أثناء التهيئة؟

استخدم init مخصص مع الوصول إلى property wrapper عبر الشرطة السفلية: _viewModel = StateObject(wrappedValue: MyViewModel(param: value)). هذا النمط يسمح بتمرير أي معاملات إلى ObservableObject مع الحفاظ على ضمان إنشاء الكائن لمرة واحدة خلال عمر الـ View.

الملخص

  • @StateObject — property wrapper لإنشاء وامتلاك ObservableObject داخل View، متاح من iOS 14.
  • ضمان الإنشاء الفردي — يتم تهيئة الكائن مرة واحدة ولا يُعاد إنشاؤه عند إعادة بناء الـ View.
  • مصدر الحقيقة — @StateObject هو مصدر الحقيقة، بينما @ObservedObject هو مجرد مراقب.
  • دورة الحياة — يعيش الكائن طالما أن الـ View موجودة في تسلسل SwiftUI ويتم تدميره عند مغادرتها.
  • التهيئة مع معاملات — تتطلب الوصول إلى property wrapper عبر _viewModel و StateObject(wrappedValue:).
  • خطأ الملكية — استخدام @ObservedObject لإنشاء كائن يؤدي إلى فقدان البيانات عند إعادة البناء.
  • كائن واحد — @StateObject واحد — للبيانات المشتركة، أنشئ @StateObject في View الجذر ومرره إلى الفروع عبر @ObservedObject.

سنقوم بتطوير تطبيق جوال جاهز

تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.

مناقشة المشروع

اقرأ أيضًا