@ObservedObject: ما هو، كيف يعمل وأمثلة

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

@ObservedObject هو Property Wrapper في SwiftUI لمراقبة مثيل ObservableObject يتم تمريره من الخارج. على عكس @StateObject، @ObservedObject لا ينشئ كائنًا — بل يشترك في التغييرات لكائن موجود بالفعل. وفقًا لـ توثيق Apple للمطورين (2025)، يُستخدم @ObservedObject في طرق العرض الفرعية التي تحتاج إلى تتبع البيانات التابعة للوالد. @ObservedObject يوفر اتصالًا تفاعليًا دون إدارة دورة حياة الكائن.

الخلاصة

  • @ObservedObject — Property Wrapper لمراقبة ObservableObject بدون ملكية
  • بدون إنشاء — يتم تمرير الكائن من طريقة العرض الأصلية أو البيئة
  • @Published — الخصائص داخل ObservableObject التي يتتبع SwiftUI تغييراتها
  • إعادة الرسم — عندما تتغير خاصية @Published، يقوم SwiftUI بتحديث جميع طرق العرض المشتركة
  • لا تخلط مع @StateObject — @ObservedObject لا يضمن مثيلًا واحدًا

ما هو @ObservedObject في SwiftUI؟

@ObservedObject هو Property Wrapper يشترك طريقة العرض في تغييرات ObservableObject. ObservableObject هو بروتوكول من إطار Combine يتطلب تنفيذ publisher objectWillChange. عندما تتغير أي خاصية موسومة بـ @Published داخل ObservedObject، يرسل publisher إشارة، ويقوم SwiftUI بإعادة رسم جميع طرق العرض المشتركة عبر @ObservedObject.

السمة الرئيسية لـ @ObservedObject هي غياب الملكية. طريقة العرض ليست مسؤولة عن إنشاء الكائن أو تدميره. يتم إنشاء الكائن في طريقة العرض الأصلية (عبر @StateObject) أو حقنه عبر @EnvironmentObject. طريقة العرض الفرعية تراقب التغييرات فقط وتتلقى التحديثات. إذا تم استبدال الكائن في الأصل، يتحول @ObservedObject إلى المثيل الجديد.

@ObservedObject مناسب لـ سيناريوهات مشاركة البيانات: نموذج المستخدم، الإعدادات المشتركة، حالة الاتصال بالخادم. عندما تحتاج طرق عرض متعددة على مستويات هرمية مختلفة إلى عرض نفس البيانات، ينشئ @ObservedObject في كل طريقة عرض اشتراكات مستقلة ولكن متسقة لمصدر واحد.

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

الفرق بين @ObservedObject و @StateObject هو أحد أكثر الأسئلة شيوعًا في مقابلات SwiftUI. القاعدة الأساسية: @StateObject ينشئ الكائن ويمتلكه، @ObservedObject يراقب كائنًا موجودًا بالفعل. انتهاك هذه القاعدة يؤدي إلى فقدان غير متوقع للبيانات أو تهيئة مزدوجة.

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

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

كيف يتتبع @ObservedObject التغييرات

آلية التتبع لـ @ObservedObject تعتمد على Combine وبروتوكول ObservableObject. أثناء التهيئة، يستدعي SwiftUI publisher objectWillChange — يجب على الكائن إصدار إشارة قبل تغيير خاصية @Published. ينقل Combine الإشارة إلى رسم بياني للتبعيات في SwiftUI، والذي يضع علامة على جميع طرق العرض التابعة على أنها بحاجة إلى تحديث. يحدث هذا بشكل متزامن قبل تغيير القيمة.

swift
class WeatherService: ObservableObject {
    @Published var temperature: Double = 22.0
    @Published var city: String = "Moscow"
}

struct WeatherView: View {
    @ObservedObject var weather: WeatherService

    var body: some View {
        VStack {
            Text("\(weather.city)")
            Text("\(weather.temperature)°C")
        }
    }
}

في المثال WeatherService هو ObservableObject بخاصيتين @Published. WeatherView يعلن @ObservedObject var weather: WeatherService، متلقيًا المثيل من الأصل. عندما تتغير temperature، يتم تشغيل objectWillChange قبل تعيين القيمة الجديدة، يعيد SwiftUI رسم WeatherView، وتظهر درجة الحرارة الفعلية. تتم إدارة الاشتراك تلقائيًا بواسطة SwiftUI — لا يحتاج المطور إلى استدعاء sink أو dispose.

أنماط استخدام @ObservedObject

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

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

النمط الثالث هو تكوين عدة ObservableObject. في التطبيقات المعقدة، يمكن للعرض مراقبة عدة كائنات: @ObservedObject var user: UserService, @ObservedObject var network: NetworkMonitor. هذا يفصل المسؤوليات بين الخدمات ويحافظ على قابلية اختبار كل مكون.

swift
struct DashboardView: View {
    @ObservedObject var user: UserViewModel
    @ObservedObject var network: NetworkMonitor

    var body: some View {
        VStack {
            Text("Welcome, \(user.name)")
            HStack {
                Circle()
                    .fill(network.isConnected ? Color.green : Color.red)
                    .frame(width: 10, height: 10)
            }
        }
    }
}

DashboardView يراقب UserViewModel وNetworkMonitor. كل كائن يتعامل مع مجال بياناته الخاص ويُعلم العرض بشكل مستقل بالتغييرات. إذا انقطعت الشبكة، يغير NetworkMonitor isConnected، ويعيد SwiftUI رسم DashboardView، محدثًا لون المؤشر. تكوين ObservableObject هو الطريقة المفضلة لتنظيم البيانات في تطبيقات SwiftUI.

@Published: الرابط بين ObservableObject وSwiftUI

@Published هو Property Wrapper من Combine يضيف تلقائيًا publisher إلى خاصية داخل ObservableObject. عندما تتغير خاصية @Published، يُنشئ Combine حدثًا عبر publisher objectWillChange. يشترك SwiftUI في هذا publisher عند استخدام @ObservedObject أو @StateObject ويعيد رسم العرض مع كل قيمة جديدة.

@Published يدعم جميع الأنواع، بما في ذلك الاختيارية والمجموعات والهياكل المخصصة. ومع ذلك، بالنسبة للمجموعات (المصفوفات والقواميس)، يتتبع SwiftUI فقط استبدال المرجع، وليس تغيير المحتوى. لاكتشاف إضافة أو إزالة عنصر، تحتاج إلى إعادة تعيين المجموعة بالكامل أو استخدام ObservableObject مع objectWillChange.send() يدوي.

تفصيل مهم: @Published يجب أن يُستخدم فقط داخل class ينفذ ObservableObject. استخدام @Published خارج ObservableObject سيؤدي إلى خطأ في الترجمة. أيضًا، لا يمكن تطبيق @Published على خصائص التهيئة البطيئة (lazy var) أو الخصائص المحسوبة.

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

الخطأ الأكثر خطورة هو استخدام @ObservedObject لإنشاء كائن. إذا كتبت @ObservedObject var model = UserViewModel() في العرض الأصل، كل رسم سينشئ مثيلًا جديدًا من UserViewModel. سيتم فقدان البيانات وإعادة إنشاء اشتراكات @Published. استخدم دائمًا @StateObject للإنشاء و@ObservedObject فقط لاستقبال كائن جاهز.

الخطأ الثاني هو تعديل خصائص @Published خارج السلسلة الرئيسية. يستخدم ObservableObject Combine الذي يتطلب إرسال التغييرات على السلسلة الرئيسية (main actor). إذا غيرت @Published في قائمة انتظار خلفية، قد يعيد SwiftUI رسم العرض في وقت غير مناسب، مما يسبب حالات سباق. استخدم DispatchQueue.main.async أو @MainActor للتحديثات.

المشكلة الثالثة هي التحديثات الدورية. إذا أدى تغيير @Published إلى آثار جانبية تغير @Published مرة أخرى، يحدث حلقة لا نهائية من إعادة الرسم. الحل: استخدام أعلام حماية (isUpdating) أو فصل المنطق عبر ObservableObject مختلف بحدود مسؤولية واضحة.

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

هل يمكن أن يكون @ObservedObject اختياريًا؟

نعم، SwiftUI يدعم @ObservedObject var model: UserViewModel?. ومع ذلك، لن تشترك طريقة العرض في التغييرات طالما أن الكائن nil. عند تعيين قيمة، يتم تنشيط الاشتراك تلقائيًا.

ما الفرق بين @ObservedObject و @EnvironmentObject؟

@ObservedObject يستقبل الكائن عبر المُهيئ، @EnvironmentObject عبر بيئة SwiftUI. @EnvironmentObject لا يتطلب تمريرًا صريحًا عبر المنشئات، ولكن يجب حقن الكائن على المستوى الأعلى من التسلسل الهرمي.

كيف أُعلم SwiftUI يدويًا بتغيير ObservableObject؟

استدع objectWillChange.send() قبل تغيير الخاصية. هذا مفيد عندما لا يكون @Published مناسبًا (على سبيل المثال، للخصائص المحسوبة أو عمليات المجموعات حيث تحتاج إلى الإبلاغ عن التغيير قبل التعديل).

لماذا لا يعيد @ObservedObject رسم العرض عند التغيير داخل مصفوفة؟

@ObservedObject و @Published يتتبعان استبدال المرجع، وليس تغيير محتوى المجموعة. لإعادة الرسم، تحتاج إلى إعادة تعيين المصفوفة: items.append(newItem) → items = items أو استخدام objectWillChange.send() قبل التعديل.

هل يمكن استخدام @ObservedObject في struct لا ينفذ View؟

لا، @ObservedObject هو Property Wrapper خاص بـ SwiftUI متاح فقط داخل الأنواع التي تنفذ بروتوكول View. للهياكل العادية، استخدم Combine مباشرة مع ObservableObjectPublisher.

الملخص

  • @ObservedObject — Property Wrapper لمراقبة ObservableObject بدون ملكية
  • @StateObject — ينشئ الكائن، @ObservedObject — يراقب كائنًا موجودًا
  • @Published — publisher تلقائي لخصائص ObservableObject
  • الاشتراك — SwiftUI يدير تلقائيًا اشتراك Combine عند استخدام @ObservedObject
  • التكوين — يمكن للعرض مراقبة عدة ObservableObject في وقت واحد
  • Main actor — يجب تغيير خصائص @Published فقط على السلسلة الرئيسية
  • EnvironmentObject — بديل لـ @ObservedObject للتمرير الضمني عبر البيئة

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

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

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

اقرأ أيضًا