Strong Reference (المرجع القوي): ما هو، آلية العمل و ARC

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

Strong Reference (المرجع القوي) « هي آلية قياسية لإدارة الذاكرة حيث يبقى الكائن في الذاكرة طالما يوجد مرجع نشط واحد على الأقل يشير إليه. على عكس المراجع الضعيفة، المرجع القوي يزيد عداد مراجع الكائن ويمنع تحريره التلقائي. وفقاً لوثائق Apple Developer Documentation، يدير ARC تلقائياً دورة حياة الكائنات في Swift و Objective-C. فهم عمل المراجع القوية أمر بالغ الأهمية لمنع تسرب الذاكرة والتبعيات الدائرية في التطبيقات المحمولة.

النقاط الرئيسية

  • Strong Reference — مرجع يحتفظ بالكائن في الذاكرة بزيادة retain count بمقدار 1.
  • ARC يقوم تلقائياً بإدراج عمليات retain و release، مما يلغي الحاجة إلى إدارة الذاكرة اليدوية في Swift و Objective-C.
  • Retain cycle يحدث عندما يشير كائنان إلى بعضهما البعض عبر مراجع قوية — لا يتم تحرير الذاكرة أبداً.
  • Weak Reference لا يزيد عداد المراجع ويصبح nil تلقائياً عند تحرير الكائن.
  • Unowned Reference لا يزيد العداد ولكنه يفترض أن عمر الكائن لا يتجاوز عمر المالك.

ما هو Strong Reference؟

Strong Reference هو نوع من المراجع إلى كائن يمنع تدميره بواسطة جامع القمامة أو نظام إدارة الذاكرة. طالما يوجد مرجع قوي واحد على الأقل للكائن، لا يتم تحرير ذاكرته. هذه هي الآلية الأساسية التي تقوم عليها ARC في Swift و Objective-C وجمع القمامة في Java و Kotlin.

مفهوم المرجع القوي أساسي لجميع اللغات ذات إدارة الذاكرة التلقائية. في الأنظمة التي تستخدم ARC، كل مرجع قوي يزيد عداد مراجع الكائن. عندما يصل العداد إلى الصفر، يتم تحرير الكائن فوراً. في Java و Kotlin مع جمع القمامة، يضمن المرجع القوي أن الكائن قابل للوصول ولن يتم جمعه بواسطة GC.

وفقاً لـ WWDC 2021، حوالي 35% من تسربات الذاكرة في تطبيقات iOS مرتبطة بالاستخدام غير الصحيح للمراجع القوية ودورات الاحتفاظ. في تطوير Android، تسرب الذاكرة عبر المراجع القوية الضمنية في closures و callbacks هو ثاني أكثر أسباب مشاكل الذاكرة شيوعاً بعد Context Leak.

للعمل بفعالية مع الذاكرة، تحتاج إلى فهم الفرق بين المراجع strong و weak و unowned واختيار نوع المرجع المناسب بناءً على الملكية وعمر الكائنات.

كيف غير ARC إدارة الذاكرة

قبل ARC، كان المطورون يستدعون retain و release يدوياً لكل كائن، مما أدى إلى العديد من الأخطاء. ARC، الذي قدمته Apple في 2011 مع LLVM 3.0، أتمت هذه العملية من خلال تحليل رسم بياني للملكية في وقت الترجمة. المترجم نفسه يقوم بإدراج استدعاءات retain و release و autorelease في الأماكن المناسبة.

وفقاً لـ Clang Static Analyzer، أدى إدخال ARC إلى تقليل الأخطاء المتعلقة بالذاكرة في تطبيقات iOS بنسبة 70%. بالنسبة للمطورين، هذا يعني أن إدارة الذاكرة أصبحت أكثر أماناً، ولكن في الوقت نفسه ظهرت الحاجة إلى فهم كيفية عمل المراجع القوية تحت الغطاء — لتجنب retain cycles.

في Kotlin و Java، يلعب جامع القمامة دور ARC، لكن مبدأ المرجع القوي يبقى كما هو: GC Roots هي نقاط الدخول التي يتم من خلالها الاحتفاظ بالكائنات بواسطة مراجع قوية. طالما أن الكائن قابل للوصول عبر سلسلة من المراجع القوية من GC Root، لن يتم جمعه.

كيف يعمل Strong Reference في ARC؟

ARC (Automatic Reference Counting) يعمل عن طريق عد المراجع لكل كائن في heap. عند إنشاء مرجع قوي جديد لكائن، يزداد العداد (retain). عند تدمير المرجع أو استبداله، ينخفض العداد (release). عندما يصل العداد إلى الصفر، يتم إزالة الكائن فوراً من الذاكرة.

لنفكر في مثال بلغة Swift. عند إنشاء مثيل لفئة، يقوم ARC بتخصيص الذاكرة وتحديد retain count إلى 1. كل تعيين جديد لمتغير آخر يزيد العداد. عندما يخرج المتغير من النطاق، ينخفض العداد:

swift
class ProfileViewController {
    var nameLabel: String?
    var avatarImage: UIImage?

    func loadProfile() {
        // retain count = 1 لمثيل جديد
        let user = User(name: "Ivan")
        // retain count = 2 بعد تعيين nameLabel
        nameLabel = user.name
        // خروج من الدالة — user يخرج من النطاق، retain count = 1
    }
}

في هذا الكود، يضمن ARC أن كائن User يبقى في الذاكرة طالما يوجد مرجع قوي واحد على الأقل يشير إليه. عندما تنتهي دالة loadProfile، يتم تدمير المتغير المحلي user، لكن nameLabel لا يزال يحتفظ بالكائن. سيتم تحرير الذاكرة فقط عندما يتوقف nameLabel عن الوجود أو يتم استبداله.

في Kotlin، يتم توفير سلوك مماثل عبر GC Roots. طالما توجد سلسلة قابلة للتتبع من المراجع القوية من جذر جامع القمامة (مثل حقل ثابت أو مؤشر ترابط نشط)، يبقى الكائن في الذاكرة. الفرق هو أن GC لا يحرر الذاكرة فوراً — يحدث ذلك بشكل غير متزامن بعد تحليل إمكانية الوصول.

متى يحدث تحرير الذاكرة

في ARC، يحدث التحرير بشكل متزامن عند وصول العداد إلى الصفر. في Swift و Objective-C، تعرف بالضبط متى سيتم إزالة الكائن. في Kotlin و Java، لحظة التحرير غير متوقعة، ولكن يتم تعويض ذلك بمخطط أكثر مرونة لاكتشاف التبعيات الدائرية على مستوى جامع القمامة.

Retain Cycles وتسرب الذاكرة

Retain cycle (دورة الاحتفاظ) — هي حالة يكون فيها لكائنين أو أكثر مراجع قوية متبادلة لبعضهما البعض. ونتيجة لذلك، لا ينخفض retain count أبداً إلى الصفر، ولا يتم تحرير الذاكرة حتى بعد أن تصبح الكائنات غير ضرورية للتطبيق.

مثال كلاسيكي: وحدة تحكم عرض رئيسية تحمل كائناً فرعياً بمرجع قوي، وهو بدوره يحمل الوالد بمرجع قوي. هذا نموذجي في الحالات مع المندوبين و closures وتعبيرات lambda المتداخلة. وفقاً لـ Instruments Leaks، تشكل retain cycles ما يصل إلى 60% من جميع تسربات الذاكرة في التطبيقات التي تستخدم ARC.

swift
class ParentViewController: UIViewController {
    var child: ChildViewController?

    func setupChild() {
        child = ChildViewController()
        // retain cycle: الوالد يحمل الطفل، الطفل يحمل الوالد عبر closure
        child?.onEvent = {
            self.handleEvent()
        }
    }

    func handleEvent() {}
}

المشكلة هنا هي أن closure onEvent تلتقط self (ParentViewController) بمرجع قوي، و ParentViewController نفسه يحمل child بمرجع قوي. لن يتم تحرير كلا الكائنين أبداً. الحل هو استخدام weak self في closure لكسر الدورة.

في Kotlin، تنشأ دورات مماثلة عند استخدام lambdas التي تلتقط كائنات خارجية. يمكن لجامع القمامة في JVM اكتشاف مثل هذه الدورات بمرور الوقت، ولكن فقط إذا كانت الكائنات غير قابلة للوصول من GC Roots. إذا كانت الدورة مرتبطة بمؤشر ترابط نشط أو سياق UI، يبقى التسرب طوال عمر التطبيق.

Strong vs Weak vs Unowned Reference

فهم الفرق بين أنواع المراجع هو مفتاح إدارة الذاكرة الآمنة. Strong Reference يزيد retain count. Weak Reference لا يزيد retain count ويصبح nil تلقائياً عند تحرير الكائن. Unowned Reference أيضاً لا يزيد retain count ولكنه لا يصبح صفراً — الوصول إليه بعد التحرير يسبب تعطل التطبيق.

نوع المرجعRetain countالأمانمتى الاستخدام
Strong+1آمن (افتراضي)ملكية الكائن، علاقة الوالد → الطفل
Weakلا يتغيرإلغاء تلقائي (آمن)المندوبون، callbacks، المراجع العكسية
Unownedلا يتغيرخطر تعطل عند الوصول المتأخرعندما يعيش الكائن لفترة أطول من المالك

اختيار نوع المرجع تمليه علاقة الملكية. إذا كان الكائن B جزءاً من A ولا يمكن أن يوجد بدونه — استخدم Strong. إذا كان B يمكن أن يوجد بشكل مستقل ويشير إلى A للإشعارات — استخدم Weak. Unowned نادر الاستخدام — فقط عندما لا يتجاوز عمر الكائن الفرعي عمر الأصل.

قاعدة الاختيار العملية

Apple Developer Documentation توصي: افتراضياً، استخدم strong لجميع علاقات الملكية. إذا كنت بحاجة لتجنب retain cycle — حدد أي مرجع يجب أن يكون ضعيفاً. عادة ما يكون هذا هو المرجع العكسي في التسلسل الهرمي (الطفل → الوالد). في Kotlin، دور مشابه يؤديه WeakReference من java.lang.ref، والذي يستخدم للـ caches وأنماط observer.

كيفية إصلاح مشاكل المراجع القوية

اكتشاف retain cycles هو الخطوة الأولى. الثانية هي إزالتها بشكل صحيح. الأداة الرئيسية لكسر دورات المراجع القوية هي استبدال أحد المراجع بـ weak أو unowned. في اللغات مع جمع القمامة، يُستخدم WeakReference مع التحقق اليدوي من null قبل كل وصول.

في Swift و Objective-C، الإصلاح الأكثر شيوعاً هو إضافة [weak self] في closures. هذا يضمن أن closure لا يحتفظ بالكائن بعد تحريره. في Kotlin، تُستخدم أغلفة WeakReference أو التنظيف الصريح للمراجع في onDestroy لأغراض مماثلة.

swift
class NetworkService {
    func fetchData(completion: @escaping (Data?) -> Void) {
        // التقاط عبر weak self — تم استبعاد retain cycle
        URLSession.shared.dataTask(
            with: URL(string: "https://api.example.com")!
        ) { [weak self] data, response, error in
            guard let self else { return }
            completion(data)
        }.resume()
    }
}

في هذا المثال، يضمن [weak self] أن NetworkService لا يتم الاحتفاظ به بواسطة closure بعد أن لم يعد مطلوباً. إذا تم تحرير self قبل اكتمال الطلب — guard let self else { return } يخرج من closure دون استدعاء completion.

لتشخيص retain cycles، استخدم Instruments Leaks لنظام iOS أو Android Profiler + LeakCanary لنظام Android. تظهر هذه الأدوات الرسم البياني الدقيق للاحتفاظ وتشير إلى أي مرجع قوي يمنع تحرير الكائن. يجب أن يكون التنميط المنتظم للذاكرة جزءاً من خط أنابيب CI/CD لأي مشروع محمول.

Strong Reference في Swift و Kotlin — مقارنة

Swift و Kotlin يستخدمان آليات إدارة ذاكرة مختلفة جوهرياً، لكن مفهوم المرجع القوي موجود في كليهما. Swift يستخدم ARC مع التحرير المتزامن عند retain count = 0. Kotlin يستخدم GC تتبعي ينظف بشكل غير متزامن الكائنات غير القابلة للوصول.

المعاملSwift (ARC)Kotlin (JVM GC)
الآليةعد المراجع (retain count)تتبع إمكانية الوصول (GC Roots)
التحريرمتزامن (عند وصول العداد للصفر)غير متزامن (بدورة GC)
Retain cycleلا يتم اكتشافه تلقائياًGC قد يكتشفه، لكن ليس فوراً
المرجع الضعيفweak (إلغاء تلقائي)WeakReference (تحقق يدوي)

الفرق العملي الرئيسي: في Swift، retain cycle هو تسرب مضمون. في Kotlin، يمكن لـ GC كسر الدورة إذا كانت الكائنات غير قابلة للوصول من الجذر، لكن عمر الكائنات المتسربة يظل غير متوقع. لذلك، في كلا اللغتين، أفضل استراتيجية هي تجنب دورات المراجع القوية في مرحلة التصميم.

بالنسبة لـ Swift، استخدم weak في أنماط المندوبين و closures. بالنسبة لـ Kotlin، استخدم WeakReference أو مكونات Lifecycle-aware التي تمسح المراجع تلقائياً عند تدمير المالك. في كلا النهجين، الهدف واحد — القضاء على المراجع القوية حيث تخلق سلسلة احتفاظ غير قابلة للكسر.

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

كيف يختلف Strong Reference عن Weak Reference؟

Strong Reference يزيد retain count للكائن ويمنع تحريره طالما المرجع موجود. Weak Reference لا يغير retain count ويصبح nil تلقائياً عند إزالة الكائن من الذاكرة. تستخدم المراجع القوية للملكية، والضعيفة للاتصالات العكسية والمندوبين.

ما هو retain cycle ولماذا هو خطير؟

Retain cycle — قفل متبادل حيث يحتفظ كائنان ببعضهما البعض بمراجع قوية. لا ينخفض retain count أبداً إلى الصفر، لا يتم تحرير الذاكرة. يؤدي هذا إلى تسرب الذاكرة: تبقى الكائنات في heap إلى الأبد، يستهلك التطبيق المزيد والمزيد من الموارد وفي النهاية ينهار مع OutOfMemory.

كيف تكتشف retain cycle في تطبيق iOS؟

استخدم Instruments Leaks من Xcode — شغل التنميط مع قالب Leaks، نفذ سيناريو في التطبيق وتحقق من مؤشرات التسرب. للتشخيص الدقيق، انتقل إلى علامة التبويب Cycles & Roots — تظهر رسم بياني للمراجع القوية المتبادلة التي تشكل دورة غير قابلة للكسر.

متى يجب استخدام Unowned بدلاً من Weak؟

Unowned يجب استخدامه عندما يكون عمر الكائن الفرعي مضموناً ألا يتجاوز عمر الأصل — على سبيل المثال، عند ربط كائن بنطاق محدد بدقة. إذا كنت في شك، استخدم Weak، لأن الوصول إلى مرجع unowned محرر يسبب تعطل التطبيق.

هل تؤثر المراجع القوية على أداء التطبيق؟

بشكل غير مباشر — نعم. كل retain و release في ARC هي عملية ذرية بتكلفة إضافية. مع عدد كبير من الكائنات في دورات، يمكن أن يؤثر ذلك على الأداء. لكن المشكلة الرئيسية ليست سرعة ARC، بل تسرب الذاكرة بسبب نوع مرجع تم اختياره بشكل غير صحيح.

الخلاصة

  • Strong Reference — الآلية الأساسية لملكية الكائن، تحتفظ به في الذاكرة عبر زيادة retain count.
  • ARC يؤتمت إدارة الذاكرة في Swift و Objective-C، ويلغي retain و release اليدويين، لكنه لا يحمي من retain cycles.
  • Retain cycle يحدث مع مراجع قوية متبادلة — هذا هو السبب الرئيسي لتسرب الذاكرة في أنظمة ARC.
  • المراجع Weak و Unowned تكسر دورات المراجع القوية دون زيادة retain count.
  • اختيار نوع المرجع تحدده علاقة الملكية: Strong للوالد→الطفل، Weak أو Unowned للطفل→الوالد.
  • Instruments Leaks و LeakCanary — الأدوات الرئيسية لاكتشاف المراجع القوية الإشكالية في iOS و Android.
  • صمم الرسم البياني للملكية مسبقاً — هذا أرخص من إصلاح تسرب الذاكرة بعد إطلاق التطبيق.

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

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

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

اقرأ أيضًا