Unowned Reference (مرجع غير مملوك) هو مرجع غير مالك في Swift لا يزيد من retain count للكائن، وعلى عكس weak، لا يتم تعيينه إلى nil بعد تحرير الكائن. وفقاً لـ Apple Swift Language Guide, 2026، يُستخدم unowned عندما يكون مضموناً أن الكائن يعيش على الأقل طالما يعيش الكائن الذي يشير إليه. على عكس Weak Reference، لا يتطلب unowned unwrap — إنه نوع غير اختياري، مما يجعل الكود أنظف لكنه يضع المسؤولية على المطور لضمان عمر الكائن.
النقاط الرئيسية
Unowned Reference هو مرجع غير مالك لكائن في ARC لا يزيد retain count الخاص به. على عكس weak، المرجع unowned لا يُصفّر بعد تحرير الكائن: يستمر في الإشارة إلى ذاكرة تم تحريرها بالفعل. الوصول إلى مثل هذا المرجع يسبب انهياراً في وقت التشغيل مع EXC_BAD_ACCESS.
مصطلح «غير مملوك» يعكس الدلالات: الكائن موجود، لكن لا أحد مسؤول عن عمره. المطور يصرح صراحة: «أضمن أن هذا الكائن سيكون حياً طالما أشير إليه». المترجم لا يتحقق من هذا الضمان — إنه عقد على مستوى المطور.
وفقاً لـ Swift.org Documentation, 2026، المراجع unowned مفضلة على weak في السيناريوهات ذات العمر المضمون لأنها: لا تتطلب نوعاً اختيارياً (كود أنظف)، لا تتطلب unwrap (تقليل force-unwrap أو guard let)، وليس لها عبء إدارة جدول weak للتصفير. لكن أي خرق للعقد يؤدي إلى انهيار.
في Swift، تُصرح المراجع unowned بالكلمة المفتاحية unowned قبل let أو var. على عكس weak، يمكن أن يكون unowned كلاً من let و var، ولا يتطلب نوعاً اختيارياً. هذه الخاصية تجعل unowned مناسباً للمراجع التي لا يمكن أن تكون nil حسب منطق المجال.
class Country {
let name: String
var capital: City! // سيتم تعيينه بعد التهيئة
init(name: String) { self.name = name }
}
class City {
let name: String
unowned let country: Country // ✅ unowned let — ضمان العمر
init(name: String, country: Country) {
self.name = name
self.country = country
}
}
// الاستخدام
let france = Country(name: "France")
let paris = City(name: "Paris", country: france)
france.capital = paris
// ✅ Country → City (strong), City → Country (unowned) — لا retain cycle
في هذا المثال، City unowned let country — المدينة لا يمكن أن توجد بدون بلد. إذا اختفى البلد، تفقد المدينة (والمرجع) معناها. من الناحية الدلالية، هذه حالة مثالية لـ unowned: ضمان العمر موجود، لا حاجة للاختياري، لا يحدث retain cycle.
unowned var مسموح به لكنه أقل شيوعاً. يُستخدم عندما يمكن استبدال المرجع (مثلاً، إعادة ربط طفل بأب مختلف). عند الاستبدال، تحرير الكائن القديم هو مسؤولية المالك الخارجي.
في Swift 5.0+، تم تقديم دعم unowned الاختياري (unowned let x: Type?). هذا حل وسط: unowned يضمن أنه إذا لم يكن المرجع nil، فالكائن حي. السلوك عند التحرير هو انهيار، كما هو الحال مع unowned العادي.
الاختيار بين unowned و weak هو أحد القرارات المتكررة عند تصميم بنية Swift. دعنا نفحص المعايير والتوصيات لكل حالة.
| المعيار | Weak | Unowned |
|---|---|---|
| اختياري | نعم (Type?) | لا (Type) |
| التصفير عند التحرير | تلقائي إلى nil | لا (خطر مؤشر معلق) |
| النوع (let/var) | var فقط | let أو var |
| الأداء | عبء جدول weak | أدنى حد (مؤشر بسيط) |
| الأمان | آمن (nil يُفحص) | خطر EXC_BAD_ACCESS |
| ضمان العمر | غير مطلوب | مطلوب ضمان صريح |
استخدم weak إذا كان هناك أدنى شك حول عمر الكائن. Weak آمن وواضح ولا يتطلب دليلاً. استخدم unowned فقط عندما تتمكن من استبعاد جميع السيناريوهات التي يمكن فيها تحرير الكائن مبكراً. الحالات النموذجية: طفل لا يوجد بدون أب؛ closure ينفذ بشكل متزامن؛ الوصول إلى كائن داخل المُهيئ الخاص به.
وفقاً لـ Airbnb Swift Style Guide, 2025، في قواعد الأكواد الكبيرة يُوصى باستخدام weak افتراضياً و unowned فقط مع تعليق صريح يشرح ضمان العمر. هذا يقلل من خطر الأعطال غير الواضحة أثناء إعادة الهيكلة.
الـ closures هي ثاني أكثر حالات استخدام unowned شيوعاً بعد علاقات الأب-الابن. قائمة الالتقاط [unowned self] تُستخدم عندما يكون مضموناً أن self يعيش أطول من closure. دعنا نفحص السيناريوهات الصحيحة والخاطئة.
الـ closures المتزامنة — sorted, filter, map. يتم تنفيذها فوراً في الخيط الحالي، self بالتأكيد حي. قائمة الالتقاط مع unowned مقبولة هنا وتنتج كوداً أنظف.
class DataProcessor {
var items: [Int] = [3, 1, 4, 1, 5]
func processSorted() {
// ✅ unowned self — sorted ينفذ بشكل متزامن، self مضمون البقاء
let sorted = items.sorted { [unowned self] a, b in
return self.customCompare(a, b)
}
}
func customCompare(_ a: Int, _ b: Int) -> Bool { return a < b }
}
الـ closures غير المتزامنة — مع التأخيرات، طلبات الشبكة، الرسوم المتحركة. قد يتم تحرير self بين جدولة closure وتنفيذه. هنا unowned self يؤدي إلى انهيار. استخدم [weak self].
class NetworkLoader {
func loadData() {
// ❌ خطر: unowned self في closure غير متزامن
URLSession.shared.dataTask(with: url) { [unowned self] data, _, _ in
self.handleResponse(data) // انهيار إذا تم تحرير self
}.resume()
}
func handleResponse(_ data: Data?) { }
// ✅ صحيح: weak self + guard
func loadDataSafe() {
URLSession.shared.dataTask(with: url) { [weak self] data, _, _ in
guard let self else { return }
self.handleResponse(data)
}.resume()
}
}
تذكر القاعدة: unowned self — فقط للـ closures المتزامنة التي تنفذ فوراً. للـ closures غير المتزامنة، استخدم دائماً weak self + guard let. استثناء: إذا كنت تحتفظ صراحة بمرجع للكائن حتى يكتمل closure (مثلاً، الاحتفاظ بمرجع قوي في متغير آخر).
Unowned أداة قوية لكنها خطيرة. دعنا نفحص سيناريوهات واقعية حيث يمكن أن يؤدي unowned إلى أعطال وطرق لتقليل المخاطر.
الخطر الرئيسي لـ unowned هو تغيير في منطق الأعمال يبطل ضمان العمر. مطور يعيد هيكلة الكود: يغير الملكية، يقدم تحريراً مؤجلاً، يضيف تخزيناً مؤقتاً — ويصبح المرجع unowned قنبلة موقوتة. المترجم لن يحذرك — فقط انهيار على جهاز المستخدم.
توصية: استخدم unowned فقط عندما يكون ضمان العمر واضحاً وموثقاً. أضف تعليقاً إلى كل unowned: لماذا هذا المرجع آمن وتحت أي ظروف يمكن انتهاكه.
UIKit منطقة عالية المخاطر لـ unowned. يمكن تحرير ViewController في أي لحظة أثناء التنقل (pop, dismiss)، تفريغ الذاكرة، تغييرات الاتجاه. إذا مررت ViewController إلى closure مع unowned self، قد يكون self nil عند العودة من الخلفية أو عند اكتمال الرسم المتحرك.
لتقليل المخاطر عند استخدام unowned، اتبع هذه القواعد:
// مثال: مرجع unowned موثق مع تبرير صريح
class InvoiceLineItem {
let productName: String
let price: Decimal
// unowned Invoice — InvoiceLineItem لا يمكن أن يوجد بدون Invoice.
// Invoice ينشئ Item ويزيله عند حذفه.
// الضمان: Invoice يعيش على الأقل طالما Item.
unowned let invoice: Invoice
init(productName: String, price: Decimal, invoice: Invoice) {
self.productName = productName
self.price = price
self.invoice = invoice
}
}
// هذا ضمان قوي: Invoice يزيل كل Items في deinit.
// انتهاك الضمان = خطأ في منطق الأعمال يجب إصلاحه.
توثيق الضمانات هو معيار مهني. في المشاريع الكبيرة (Airbnb, Uber)، مراجعة الكود تتطلب تبريراً لكل unowned. إذا كان الضمان غير واضح، استخدم weak. تعليق على unowned يساعد المطورين المستقبليين على فهم لماذا لم يُستخدم weak هنا وما الظروف التي قد تكسر الضمان.
الأسئلة الشائعة
انهيار في وقت التشغيل مع EXC_BAD_ACCESS. Swift لا يتحقق من صحة المرجع unowned عند الوصول — إنه مجرد مؤشر «خام». إذا تم تحرير الكائن، يتم استبدال الذاكرة، والوصول إليها ينتهي بشكل كارثي. هذا استثناء غير قابل للالتقاط (ليس try-catch).
نعم، إذا كان البروتوكول موروثاً من AnyObject. Unowned يعمل مع جميع أنواع المراجع: الكلاسات، بروتوكولات AnyObject، كائنات Objective-C. أنواع القيم (struct, enum) لا تدعم unowned لأنها لا تشارك في ARC.
عندما يكون ضمان العمر مطلقاً وواضحاً — unowned أكثر أماناً من منظور التصميم: لا يتطلب unwrap، لا يمكن أن يكون nil، ولا يخفي الأخطاء. إذا كان الكائن لا يمكن أن يوجد بدون أب، unowned يجعله عقداً صريحاً، بينما weak يضعف الضمان.
نعم: unowned أسرع لأنه لا يتطلب الوصول إلى جدول weak في وقت التشغيل للتصفير. في معظم التطبيقات الفرق غير محسوس، لكن في سيناريوهات الأحمال العالية مع ملايين الوصولات، يمكن أن يكون unowned أسرع بنسبة 10–20% في القراءة.
إعادة الهيكلة هي الخطر الرئيسي لـ unowned. تغيير عمر الكائن (التخزين المؤقت، العمليات غير المتزامنة، إعادة الاستخدام) يمكن أن يكسر الضمان. المترجم لن يحذرك. الحل: هاجر إلى weak عند تغيير البنية أو أضف تعليق تحذيري.
الخلاصة
unowned let أو unowned var؛ يمكن أن يكون غير اختياري واختياري (Swift 5.0+)سنقوم بتطوير تطبيق جوال جاهز
تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.
اقرأ أيضًا