Weak Reference (مرجع ضعيف) هو مرجع إلى كائن لا يزيد عداد الاحتفاظ به في ARC. وفقًا لـ Apple Swift Language Guide, 2026، يتم تعريف المراجع الضعيفة بالكلمة المفتاحية weak ودائمًا ما تكون اختيارية. عندما يتم تحرير الكائن، يتم تعيين جميع المراجع الضعيفة إليه تلقائيًا إلى nil، مما يمنع المؤشرات المعلقة ويجعل المراجع الضعيفة آلية آمنة لكسر دورات الاحتفاظ.
الرئيسية
weak قبل var؛ النوع دائمًا اختياري (?)Weak Reference هو مرجع غير مالك لكائن في ARC (Automatic Reference Counting). على عكس المرجع القوي، الذي يزيد retain count للكائن ويضمن عمره، يسمح المرجع الضعيف بتحرير الكائن حتى لو كان لا يزال مرجعًا إليه. بعد التحرير، يتم تعيين المرجع الضعيف تلقائيًا إلى nil — وهذا ما يسمى zeroing weak.
Zeroing weak هي ميزة رئيسية في runtime لكل من Swift و Objective-C. عندما يصل عداد مراجع الكائن إلى الصفر ويتم تحرير الكائن، يقوم runtime بمراجعة جميع المراجع الضعيفة لهذا الكائن (المخزنة في جدول ضعيف خاص) وتعيينها إلى nil. هذا يضمن أن الوصول إلى الذاكرة المحررة (use-after-free) مستحيل عبر المراجع الضعيفة — أي قراءة ترجع nil.
وفقًا لـ Apple WWDC 2012 Session 406، قامت المراجع الضعيفة zeroing بالقضاء على فئة كاملة من أخطاء الأعطال المتعلقة بالمؤشرات المعلقة (dangling pointers)، التي كانت شائعة في إدارة الذاكرة اليدوية (MRR). في MRR، كانت المراجع الضعيفة موجودة فقط كـ __unsafe_unretained — لم تكن تُصفّر، وكان الوصول إلى كائن محرر يؤدي إلى EXC_BAD_ACCESS.
دعنا ننظر إلى تركيب تعريف المراجع الضعيفة في كلتا لغتي نظام Apple البيئي. على الرغم من runtime المشترك، تختلف التركيبة، لكن الدلالات متطابقة.
في Swift، يتم تعريف المراجع الضعيفة بالكلمة المفتاحية weak قبل var. يجب أن يكون النوع دائمًا اختياريًا (Type?)، لأن المرجع يمكن أن يصبح nil في أي وقت. الثوابت (let) لا يمكن أن تكون weak — فقط المتغيرات.
class ViewController: UIViewController {
// weak properties: only var, only optional
weak var delegate: ViewControllerDelegate?
weak var parentView: UIView?
weak var completionHandler: ((Bool) -> Void)? // ⚠️ الإغلاقات لا تخزن weak
// ⬆️ خطأ: weak يمكن تطبيقه فقط على أنواع class، وليس closures
}
مهم: weak قابل للتطبيق فقط على نسخ class (أنواع class)، AnyObject، والبروتوكولات الموروثة من AnyObject. Struct و enum والإغلاقات لا يمكن أن تكون weak — فهي أنواع قيمة ولا تشارك في ARC.
في Objective-C، يتم تعريف الخصائص الضعيفة باستخدام السمة __weak أو المُعدِّل weak في تعريفات الخاصية:
// Objective-C: weak property
@interface MyViewController : UIViewController
@property (weak, nonatomic) id<MyDelegate> delegate;
@end
// متغير weak محلي
__weak MyObject *weakRef = someStrongObject;
يوفر runtime في Objective-C أيضًا zeroing weak، لكنه additionally يمنع استخدام weak مع هياكل C وبعض كائنات Core Foundation. لهذه، يتم استخدام __unsafe_unretained — بدون zeroing.
المراجع الضعيفة ليست حلاً شاملاً، بل أداة لسيناريوهات محددة. استخدام weak في كل مكان يؤدي إلى تعقيد غير ضروري ويضر بسهولة القراءة. دعنا ننظر إلى سيناريوهات الاستخدام الصحيحة.
المفوّضون — السيناريو الرئيسي لـ weak. الكائن المالك (مثل UITableView) يحمل مرجعًا قويًا لنفسه، بينما المفوّض (UIViewController) لا يجب أن يمتلك الجدول. يضمن Apple SDK أن جميع المفوّضين ومصادر البيانات هم weak. للبروتوكولات الخاصة بك، استخدم دائمًا weak var delegate.
عندما يحتاج كائن ابن إلى الإشارة إلى والده (مثل ChildViewController الوصول إلى المنسق)، استخدم مرجعًا ضعيفًا. الأب يمتلك الابن (strong)، الابن يراقب الأب (weak) — دورة الاحتفاظ مستبعدة.
Capture list [weak self] — الطريقة القياسية لتجنب دورات الاحتفاظ في الإغلاقات المخزنة كخصائص class. إذا كان self قد يُحرر قبل اكتمال الإغلاق، فإن weak self إلزامي.
| السيناريو | Weak | Strong |
|---|---|---|
| مفوّض | ✅ دائمًا weak | ❌ دورة احتفاظ |
| أب ← ابن | ❌ غير ضروري (الأب يجب أن يمتلك) | ✅ Strong |
| ابن ← أب | ✅ Weak | ❌ دورة احتفاظ |
| استدعاء غير متزامن | ✅ [weak self] | ❌ خطر دورة احتفاظ |
| اقتران قوي (owned) | ❌ unowned | ✅ Strong |
قاعدة عامة: إذا كان الكائن A يمتلك B (A → B strong)، فإن B → A يجب أن يكون weak أو unowned. اتجاه المراجع القوية يجب أن يكون دائمًا من المالك إلى التابع.
كل من weak و unowned لا يزيدان retain count، لكنهما يختلفان في السلوك بعد تحرير الكائن. الاختيار بينهما مسألة ضمانات العمر.
Weak: يصبح nil تلقائيًا، النوع دائمًا اختياري، يتطلب فك تغليف قبل الاستخدام. آمن — الوصول إلى nil لا يسبب عطلًا.
Unowned: لا يصبح nil، النوع غير اختياري. إذا تم تحرير الكائن، يصبح المرجع unowned مؤشرًا معلقًا — الوصول إليه يسبب عطلًا في وقت التشغيل. Unown يفترض أن الكائن يعيش على الأقل بقدر الجهة المرجعية.
اختر weak إذا: الكائن قد يُحرر في أي وقت (مفوّض بعد إغلاق الشاشة)، لا تتحكم في عمر الكائن، أو غير متأكد من الضمانات. Weak هو الخيار الآمن العالمي.
اختر unowned إذا: الكائن مضمون ألا يُحرر قبل الكائن المرجع (مثل Customer → CreditCard، حيث البطاقة لا توجد بدون العميل). Unowned يوفر واجهة برمجة غير اختيارية بدون فك تغليف، وهو أكثر ملاءمة في الكود.
class Order {
let id: Int
var items: [Item] = []
init(id: Int) { self.id = id }
// علاقة قوية: Order يمتلك Item
func addItem(name: String) {
let item = Item(name: name, order: self)
items.append(item)
}
}
class Item {
let name: String
unowned let order: Order // ✅ unowned — Item لا يعيش بدون Order
init(name: String, order: Order) {
self.name = name
self.order = order
}
}
// مثال مع weak: مفوّض بدون ضمان عمر
protocol NetworkServiceDelegate: AnyObject {
func didReceiveResponse(data: Data)
}
class NetworkService {
weak var delegate: NetworkServiceDelegate? // ✅ weak — المفوّض قد يختفي
}
في المثال، يستخدم Item unowned لأن عنصر الطلب لا يمكن أن يوجد بدون الطلب نفسه — ضمان العمر حديدي. يستخدم NetworkService weak لأن المفوّض (مثل ViewController) قد يُغلق ويُحرر في أي وقت.
المراجع الضعيفة أداة قوية، لكن لها قيود من المهم فهمها للاستخدام الصحيح في تطوير iOS.
المراجع الضعيفة أبطأ من القوية: في كل وصول، يتحقق runtime مما إذا كان الكائن قد تم تحريره (lookup في الجدول الضعيف). في الغالبية العظمى من السيناريوهات، الفرق غير محسوس، لكن في الحلقات الحرجة بملايين الوصولات، يمكن أن يصبح weak عنق زجاجة. للسيناريوهات عالية الحمل، استخدم strong وأعد تنظيم البنية.
Struct، enum، tuple — أنواع قيمة لا تشارك في ARC. محاولة تعريف weak struct تؤدي إلى خطأ في الترجمة. لتخزين مرجع ضعيف إلى نوع قيمة، استخدم غلافًا في نوع class أو إغلاقًا.
Zeroing weak آمن للخيوط: إذا تم تحرير كائن في خيط واحد، يتم تصفير المرجع الضعيف في جميع الخيوط بشكل ذري. ومع ذلك، يمكن أن تؤدي النافذة بين قراءة المرجع الضعيف وإلغاء الإشارة إليه إلى حالة سباق — يتم تحرير الكائن بين الحصول على المرجع الضعيف واستخدامه. الحل: التقاط قوي للمرجع الضعيف في متغير محلي.
// حالة سباق مع weak في تعدد الخيوط
func performAsync() {
weak var weakSelf = self
queue.async {
// ⚠️ weakSelf قد يكون nil بين التحقق والاستخدام
if weakSelf != nil {
weakSelf!.doSomething() // CRASH إذا أصبح nil
}
}
}
// ✅ الإصلاح: التقاط قوي أثناء الاستخدام
func performAsyncSafe() {
queue.async { [weak self] in
guard let strongSelf = self else { return }
strongSelf.doSomething() // strongSelf — مرجع محلي قوي
}
}
في النسخة الآمنة، يتم التقاط weak self ثم فك تغليفه فورًا في متغير قوي محلي strongSelf. إذا كان self لا يزال حيًا، سيبقى حيًا طوال مدة الكتلة. إذا لم يكن، يتم تفعيل guard ولا يتم تنفيذ الكود. هذا التركيب الاصطلاحي هو النمط القياسي للإغلاقات غير المتزامنة في Swift.
IBOutlet في Interface Builder يجب أن تكون weak لأن تسلسل الـ view hierarchy يحمل بالفعل مرجعًا قويًا إلى الـ subview. تكرار مرجع قوي في المتحكم لا ينشئ دورة احتفاظ لكنه زائد عن الحاجة. المرجع الضعيف للـ outlet هو توصية Apple، على الرغم من أن العديد من المطورين يستخدمون strong لتبسيط الكود.
الأسئلة الشائعة
لا، weak يمكن أن يشير فقط إلى كائن موجود أو nil. عند إنشاء كائن جديد، تحصل أولاً على مرجع قوي (عبر مُهيئ)، وعندها فقط يمكنك تعيين مرجع ضعيف. weak nil في البداية هو حالة طبيعية.
Weak يعتمد على ARC، الذي يدير فقط أنواع المرجع (class). أنواع القيمة (struct، enum) تُنسخ عند التعيين وليس لديها retain count. للعلاقات الضعيفة مع أنواع القيمة، استخدم إغلاقات أو أغلفة في class مع خاصية weak.
كل وصول إلى مرجع ضعيف يقوم lookup في جدول runtime. في حلقة بملايين التكرارات، يمكن أن يكون هذا أبطأ بمقدار 2–5 مرات من المرجع القوي. للمسارات الحرجة، انسخ weak إلى متغير قوي محلي قبل الحلقة.
عند فقدان جميع المراجع القوية للكائن — في نهاية النطاق، عند إعادة تعيين خاصية، أو عند إغلاق شاشة. في بيئة متعددة الخيوط، يمكن أن يحدث هذا بين سطرين من الكود. تحقق دائمًا من المراجع الضعيفة عبر guard let أو if let.
متطابقان دلاليًا: كلاهما يوفر zeroing weak. الاختلافات: Swift يتطلب نوعًا اختياريًا و var، Objective-C يستخدم مُعدِّل property. Objective-C also يدعم __unsafe_unretained — مرجع ضعيف بدون zeroing (خطر مؤشر معلق).
الخلاصة
weak var + نوع اختياري؛ فقط أنواع class وبروتوكولات AnyObjectسنقوم بتطوير تطبيق جوال جاهز
تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.
اقرأ أيضًا