Observer — نمط سلوكي يقوم فيه كائن واحد (الناشر) بإخطار多个 المشتركين بالتغييرات في حالته. في تطوير التطبيقات المحمولة، يشكل Observer أساس الآليات التفاعلية: تشترك واجهة المستخدم في تغييرات البيانات ويتم تحديثها تلقائياً. النمط مطبق في NotificationCenter على iOS وLiveData/Flow على Android. المزيد من التفاصيل في Refactoring Guru: Observer.
النقاط الرئيسية
Observer — نمط سلوكي من GoF يحدد علاقة واحد إلى متعدد بين الكائنات. عندما يغير كائن واحد (Subject أو Observable) حالته، يتم إخطار جميع الكائنات التابعة (Observers) وتحديثها تلقائياً. ينفذ النمط اقتراناً ضعيفاً: لا يعرف الناشر الفئات المحددة للمشتركين — فقط أنهم يطبقون واجهة Observer.
هيكل Observer يشمل واجهة Subject مع طرق attach() وdetach() وnotify() وواجهة Observer مع طريقة update(). يخزن ConcreteSubject الحالة وقائمة المشتركين. ينفذ ConcreteObserver دالة update() ويتفاعل مع التغييرات. في تطوير التطبيقات المحمولة، التطبيق الكلاسيكي لـ GoF نادر — يتم استبداله بآليات مدمجة: NotificationCenter وCombine وFlow وLiveData، التي تنفذ الفكرة نفسها بواجهات برمجية حديثة.
نموذج Push vs Pull — في نموذج Push، يرسل Subject البيانات إلى جميع المشتركين (NotificationCenter.post). في نموذج Pull، يقوم Subject فقط بالإخطار، ويطلب المشترك البيانات بنفسه. يستخدم Android LiveData نموذج Push (يتم تمرير البيانات في observe())، ويدعم RxJava/Flow كلا النموذجين. يعتمد الاختيار على المهمة: Push أبسط لتحديثات واجهة المستخدم، وPull أكثر كفاءة للكميات الكبيرة من البيانات التي قد لا يرغب المشترك في استلامها.
NotificationCenter — آلية مدمجة في iOS/macOS لتنفيذ Observer. يرسل الناشر إشعاراً عبر NotificationCenter.default.post(name:, object:, userInfo:). يسجل المشترك عبر addObserver(forName:, queue:, using:). يدعم NotificationCenter الإشعارات المسماة (Notification.Name) ويمكنه تمرير أي بيانات في userInfo. UIKeyboardWillShowNotification وUIApplicationDidEnterBackgroundNotification — أمثلة نظامية.
extension Notification.Name {
static let userDidLogin = Notification.Name("userDidLogin")
}
// الناشر
NotificationCenter.default.post(
name: .userDidLogin,
object: nil,
userInfo: ["userId": "123"]
)
// المشترك
class ProfileViewModel {
private var observers: [NSObjectProtocol] = []
func startObserving() {
let observer = NotificationCenter.default.addObserver(
forName: .userDidLogin,
object: nil,
queue: .main
) { [weak self] notification in
guard let userId = notification.userInfo?["userId"] as? String else { return }
// المشترك يتفاعل مع الحدث
self?.loadProfile(userId: userId)
}
observers.append(observer)
}
func stopObserving() {
observers.forEach { NotificationCenter.default.removeObserver($0) }
observers.removeAll()
}
}
إطار Combine — بديل تفاعلي حديث لـ NotificationCenter، تم تقديمه في iOS 13. Publisher (NotificationCenter وURLSession وTimer) — الناشر، Subscriber (sink, assign) — المشترك. يضيف Combine عوامل تشغيل (map, filter, combineLatest) لتحويل تدفق البيانات. @Published — مُغلف خصائص يخطر المشتركين تلقائياً بالتغييرات. في MVVM مع SwiftUI، يحل Combine محل NotificationCenter لربط ViewModel وView.
KVO (Key-Value Observing) — آلية قديمة في ObjC/Swift لمراقبة الخصائص الفردية للكائنات. @objc dynamic var name: String — خاصية قابلة للمراقبة. observe(.name) — اشتراك. يعمل KVO فقط مع الفئات المتوافقة مع @objc ووراثة ObjC. توصي Apple باستخدام Combine و@Published بدلاً من KVO في المشاريع الجديدة. يبقى KVO مهماً للتوافق مع UIKit في المشاريع الهجينة.
LiveData — مكون من Android Architecture Components لتنفيذ Observer. فئة قابلة للمراقبة تخطر المشتركين بتغييرات البيانات. يراعي LiveData دورة الحياة: يتم إلغاء اشتراك المشتركين (LifecycleOwner) تلقائياً عند التدمير. يستخدم LiveData نموذج Push: يتم تمرير البيانات في observe(). LiveData هو اللبنة الأساسية لـ MVVM في Android قبل Jetpack Compose.
// ViewModel — الناشر
class UserViewModel : ViewModel() {
private val _user = MutableLiveData<User?>(null)
val user: LiveData<User?> = _user
fun loadUser(id: String) {
viewModelScope.launch {
val result = userRepository.getUser(id)
_user.value = result
}
}
}
// Fragment — المشترك
class UserFragment : Fragment() {
private val viewModel: UserViewModel by viewModels()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
viewModel.user.observe(viewLifecycleOwner) { user ->
// المشترك يتفاعل مع التغييرات
userName.text = user?.name
userEmail.text = user?.email
}
}
}
StateFlow وSharedFlow — أنواع تفاعلية من Kotlin Coroutines حلت محل LiveData في Jetpack Compose. StateFlow — حامل حالة قابل للمراقبة بقيمة حالية ثابتة. SharedFlow — تدفق ساخن قابل للتكوين بدون حالة، مناسب للأحداث لمرة واحدة (التنقل، الإشعارات المنبثقة). كلا النوعين متكاملان بشكل وثيق مع Compose: collectAsState()، collectAsEffect(). StateFlow إلزامي في مشاريع Android الحديثة مع Compose.
LiveData مقابل StateFlow — LiveData مرتبط بدورة حياة Android، StateFlow مستقل عن المنصة. يدعم StateFlow coroutines وعوامل التشغيل (map, filter) ويمكن اختباره دون تبعيات Android. LiveData أبسط للتوافق مع Java. توصي Google باستخدام StateFlow للمشاريع الجديدة على Kotlin + Compose، وLiveData للمشاريع القديمة أو كود Java.
تسرب الذاكرة — المشكلة الرئيسية في Observer بدون إدارة صحيحة للاشتراكات. إذا تم تدمير مشترك (Activity أو Fragment أو UIViewController) ولكن لم يتم إلغاء اشتراكه، يستمر الناشر في الاحتفاظ بمرجع له ولا يستطيع جامع القمامة تحرير الذاكرة. في Android، يجب على LifecycleOwner (Activity/Fragment) استدعاء removeObserver() أو استخدام observe(viewLifecycleOwner). في iOS — removeObserver في deinit أو disposeBag في Combine.
| المنصة | آلية Observer | إلغاء اشتراك تلقائي | إلغاء اشتراك يدوي |
|---|---|---|---|
| iOS | NotificationCenter | لا | removeObserver() في deinit |
| iOS | Combine (sink) | لا | store(in: &bag) — DisposeBag |
| iOS | KVO | لا | removeObserver() في deinit |
| Android | LiveData | نعم (LifecycleOwner) | removeObserver() اختياري |
| Android | StateFlow | عبر viewModelScope | cancel() Job عند الإلغاء |
| Android | RxJava | لا | dispose() في CompositeDisposable |
مرجع ضعيف في المشتركين — عند استخدام closures في كتل الاشتراك، استخدم [weak self] في Swift وأشر إلى نطاق دورة الحياة في Kotlin. يدير LiveData الاشتراكات تلقائياً عبر LifecycleOwner — الاشتراك نشط فقط عندما تكون دورة الحياة في حالة STARTED أو RESUMED. يستخدم StateFlow في Compose collectAsState() مع مراعاة دورة الحياة. يتطلب NotificationCenter في iOS [weak self] صريحاً لأن closure يحتفظ بمرجع قوي إلى self.
Observer (GoF) والناشر-المشترك (PubSub) — أنماط متشابهة ولكن مختلفة. في Observer، يخطر الناشر المشتركين مباشرة عن طريق استدعاء طرقهم. يعرف الناشر المشتركين (يخزن قائمة). في PubSub، لا يعرف الناشر والمشترك بعضهما البعض — يوجد وسيط (Event Bus أو Message Queue أو NotificationCenter) بينهما. يرسل الناشر رسالة إلى قناة، ويستمع المشترك إلى القناة. PubSub لديه اقتران أضعف.
أمثلة على PubSub في تطوير التطبيقات المحمولة — يمكن اعتبار NotificationCenter في iOS PubSub: الناشر لا يعرف المشتركين —他只是 ينشر إشعاراً. EventBus أو Otto في Android (قديمان). SharedFlow مع BroadcastChannel — PubSub في عالم Kotlin. في الأنظمة الموزعة، يتم تنفيذ PubSub عبر RabbitMQ وKafka وGoogle PubSub. لتطوير التطبيقات المحمولة، PubSub مفيد في الهندسة المعيارية حيث لا يجب أن تعتمد الوحدات على بعضها البعض.
ماذا تختار — لتحديثات واجهة المستخدم (ViewModel → View)، استخدم Observer (LiveData وStateFlow و@Published). للأحداث بين الوحدات (المصادقة، تسجيل الخروج، تغيير السمة) — PubSub (SharedFlow وNotificationCenter وEventBus). Observer أبسط وأكثر كفاءة داخل شاشة واحدة، PubSub أكثر مرونة للأحداث العالمية ولكنه أصعب في التصحيح بسبب التبعيات الضمنية.
الأسئلة الشائعة
StateFlow هو نوع مستقل عن المنصة من Kotlin Coroutines، بينما LiveData مرتبط بدورة حياة Android. يدعم StateFlow coroutines وعوامل التشغيل ويمكن اختباره دون Android. يدير LiveData الاشتراكات تلقائياً عبر LifecycleOwner. توصي Google باستخدام StateFlow للمشاريع الجديدة على Kotlin + Compose، وLiveData للتوافق مع Java.
استخدم [weak self] في closure المعالج واستدع removeObserver() في deinit. خزن مرجعاً إلى observer (NSObjectProtocol) وقم بإزالته عند تدمير الكائن. في Combine، استخدم AnyCancellable وstore(in:) للإلغاء التلقائي عند تحرير DisposeBag.
نعم، يدعم SwiftUI ObservableObject مع @Published و@StateObject/@ObservedObject — هذا تطبيق مدمج لـ Observer. يخطر @Published View تلقائياً بالتغييرات. Combine ليس ضرورياً: يستخدم ObservableObject Publisher objectWillChange المدمج في SwiftUI. يضيف Combine عوامل تشغيل لتحويل التدفقات.
SharedFlow — للأحداث لمرة واحدة (التنقل، الإشعارات المنبثقة، Snackbar) حيث لا حاجة لقيمة حالية. StateFlow — لحالة واجهة المستخدم (قائمة البيانات، تقدم التحميل) حيث نحتاج لقطة حالية. SharedFlow ليس له خاصية value ولا يعيد القيمة الأخيرة للمشتركين الجدد.
KVO هي آلية قديمة من ObjC، تتطلب @objc dynamic وتعمل فقط مع الفئات الموروثة من NSObject. Combine هو إطار عمل حديث من Swift، آمن من حيث الأنواع، مع عوامل تشغيل وتكامل مع SwiftUI. يحل Combine محل KVO وNotificationCenter. توصي Apple باستخدام Combine للمشاريع الجديدة، وKVO فقط للدعم القديم.
الملخص
سنقوم بتطوير تطبيق جوال جاهز
تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.