ARC: ما هو، مبدأ عمل Automatic Reference Counting في iOS

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

Automatic Reference Counting (ARC) هو نظام إدارة ذاكرة في Swift و Objective-C يقوم تلقائياً بعد عدد المراجع لكل كائن وتحريره عندما يصل العداد إلى الصفر. وفقاً توثيق Apple Swift، 2026، يتم دمج ARC في المترجم ويعمل في مرحلة الترجمة، مدخلاً استدعاءات retain/release في الأماكن المناسبة. على عكس Garbage Collection، لا يتطلب ARC خيطاً منفصلاً للمجمع ولا يُنشئ توقفاً أثناء تنفيذ التطبيق.

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

  • ARC — Automatic Reference Counting، نظام إدارة ذاكرة قائم على المترجم في Swift و Objective-C
  • مبدأ العمل — كل كائن له عداد مراجع (retain count)، وعند الوصول إلى الصفر يتم تحرير الكائن فوراً
  • المُعدّلات — strong و weak و unowned تحدد كيف يؤثر المرجع على العداد ودورة حياة الكائن
  • الفرق عن GC — يعمل ARC بشكل حتمي في مرحلة الترجمة، دون توقف Stop-The-World أو خلفية للمجمع
  • Retain Cycle — المشكلة الرئيسية لـ ARC: إذا أشار كائنان إلى بعضهما عبر strong، فلن يصل عدادهم أبداً إلى الصفر

ما هو ARC؟

ARC (Automatic Reference Counting) هو آلية إدارة ذاكرة قائمة على المترجم قدمتها Apple في Xcode 4.2 (2011) لـ Objective-C وورثتها Swift. على عكس إدارة الذاكرة اليدوية (Manual Retain-Release، MRR)، يقوم ARC بأتمتة استدعاءات retain و release و autorelease بالكامل، مدخلاً إياها في مرحلة الترجمة دون تدخل المطور.

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

وفقاً WWDC 2011 Session 323، أدى الانتقال من MRR إلى ARC إلى تقليل أخطاء الأعطال المتعلقة بالذاكرة بنسبة 70% في تطبيقات Apple. توقف المطورون عن الموازنة اليدوية لـ retain/release، مما قضى على فئة كاملة من تسربات الذاكرة وأخطاء double-free.

كيف يعمل Automatic Reference Counting

كل كائن في الذاكرة له عداد مراجع (retain count). عند إنشاء كائن، يتم تعيين العداد إلى 1. عندما يشير مرجع strong جديد إلى الكائن — يزداد العداد (retain). عندما يختفي مرجع strong — ينخفض العداد (release). عند الوصول إلى الصفر، يتم تحرير الكائن فوراً.

يقوم مترجم Swift بإدراج retain/release ليس عند كل تعيين — بل يستخدم التحليل الثابت للتحسين. على سبيل المثال، إذا تم ضمان عدم استخدام كائن بعد تمريره، قد يتخطى المترجم release/retain غير ضروري. يُسمى هذا التحسين ARC Optimization.

swift
class Person {
    let name: String
    init(name: String) {
        self.name = name
        print("\(name) initialized (retain count: 1)")
    }
    deinit {
        print("\(name) deallocated")
    }
}

func testARC() {
    let p = Person(name: "Alice")  // retain count = 1
    let q = p                      // retain count = 2
    // q يخرج من النطاق
    // retain count = 1
    // p يخرج من النطاق
    // retain count = 0 → deinit
}

يوضح هذا المثال كيف يدير ARC العداد: عند تعيين q = p، يزداد العداد؛ عندما يخرج q من النطاق، ينخفض. عندما يختفي آخر مرجع strong، يتم استدعاء المدمر فوراً. لا ينتظر أي مجمع قمامة — يتم تحرير الذاكرة فوراً.

ARC مقابل Garbage Collection: الفروق الرئيسية

ARC و Garbage Collection يحلان نفس المشكلة — إدارة الذاكرة التلقائية — ولكن بمناهج مختلفة جوهرياً. يحدد الاختيار بينهما بنية اللغة: Swift (ARC) مقابل Java/Go (GC). دعونا نلقي نظرة على الفروق الرئيسية.

الخاصيةARC (Swift/ObjC)GC (Java/Go)
وقت التحريرحتمي: فوراً عند وصول العداد إلى الصفرغير حتمي: في دورة التجميع التالية
توقف التنفيذلا يوجد (retain/release مدخلة في الترجمة)توقف Stop-The-World (2–200 ملي ثانية)
الكلفة الإضافيةزيادة/نقصان العداد عند كل مرجعاجتياز رسم بياني للكائنات، وضع علامات، تحرير
المشاكلRetain Cycle (حل يدوي)تجزئة الكومة، تسربات من مراجع منسية
خيط إضافيغير مطلوبيتطلب خيط مجمع القمامة

المقايضة الرئيسية: ARC يوفر أعمار كائنات قابلة للتنبؤ وبدون توقف، لكنه يتطلب من المطور فهم retain cycles واختيار weak/unowned بشكل صحيح. GC يحرر المطور من هذه الاهتمامات، ولكن على حساب توقف غير حتمي وخيط إضافي.

Strong و Weak و Unowned: معدّلات المرجع في ARC

يحدد ARC ثلاثة أنواع من معدّلات المرجع، كل منها يؤثر على العداد ودورة حياة الكائن بشكل مختلف. اختيار المُعدّل الصحيح هو أساس إدارة الذاكرة الآمنة في Swift.

Strong

Strong هو المُعدّل الافتراضي. كل مرجع strong يزيد retain count للكائن بمقدار 1. طالما يوجد مرجع strong واحد على الأقل، يبقى الكائن حياً. جميع خصائص الفئة والمتغيرات المحلية في Swift هي strong بشكل افتراضي. تُنشئ المراجع strong علاقة ملكية: الكائن A يملك الكائن B.

Weak

Weak هو مرجع لا يزيد retain count. يمكن تحرير كائن حتى لو كان هناك مرجع weak يشير إليه. بعد التحرير، يتم تعيين المرجع weak تلقائياً إلى nil. تُعلن المراجع weak دائماً كـ var بنوع اختياري (?). تُستخدم لكسر retain cycles، خاصة في نمط delegate.

Unowned

Unowned هو مرجع غير مالك، مثل weak، لا يزيد retain count. ومع ذلك، لا يتم تعيين المرجع unowned إلى nil بعد التحرير — الوصول إلى كائن محرر يسبب تعطل التطبيق. يُستخدم unowned عندما يكون مضموناً أن الكائن يعيش على الأقل بقدر الكائن المُشار إليه. حالات الاستخدام النموذجية هي الإغلاقات (closures) وعلاقات الأب-الابن مع عمر مضمون.

swift
class Customer {
    let name: String
    var card: CreditCard?         // strong
    init(name: String) { self.name = name }
    deinit { print("\(name) deallocated") }
}

class CreditCard {
    let number: String
    unowned let customer: Customer   // unowned — لا يملك
    init(number: String, customer: Customer) {
        self.number = number
        self.customer = customer
    }
    deinit { print("Card \(number) deallocated") }
}

var customer: Customer? = Customer(name: "Bob")
customer?.card = CreditCard(number: "1234", customer: customer!)
customer = nil
// Customer و CreditCard كلاهما محرر — لا يوجد retain cycle

هنا تستخدم CreditCard مرجعاً unowned إلى Customer. Customer يملك البطاقة (strong)، والبطاقة لا تملك العميل (unowned). عندما يتم تحرير Customer، يتم تحرير كلا الكائنين — لا يحدث retain cycle. لو كان card.customer strong، لكانت الدورة قد منعت التحرير.

المشاكل الشائعة لـ ARC وحلولها

على الرغم من الأتمتة، ARC ليس حلاً سحرياً. يواجه المطورون عدة مشاكل نموذجية تتطلب فهماً للآلية الداخلية لإدارة الذاكرة.

Retain Cycle في الإغلاقات (Closures)

الإغلاقات (Closures) في Swift تلتقط المتغيرات الخارجية بمرجع strong. إذا تم تعيين إغلاق لخاصية فئة والتقط self — يحدث retain cycle: الفئة تحتفظ بالإغلاق، والإغلاق يحتفظ self. الحل هو قائمة التقاط مع weak أو unowned.

swift
class NetworkManager {
    var completionHandler: ((Data?) -> Void)?
    var data: Data?

    func fetchData() {
        completionHandler = { [weak self] result in
            guard let self else { return }
            self.data = result
            self.processResult()
        }
    }

    func processResult() { }
}

قائمة الالتقاط [weak self] تُنشئ مرجعاً weak إلى self داخل الإغلاق. هذا يكسر retain cycle المحتمل. Guard let self يضمن أن الكائن حي قبل تنفيذ الكود. weak self هي الممارسة القياسية للإغلاقات غير المتزامنة في Swift.

أداء retain/release

على الرغم من أن retain/release عمليات خفيفة، إلا أن الزيادات/النقصانات المتكررة للعداد في الحلقات المكثفة تُضيف كلفة إضافية. في Swift 5.9+، يستخدم المترجم تحسيناً يزيل retain/release الزائدة إذا أثبت المحلل أنها آمنة. ومع ذلك، في Objective-C، لا يزال retain/release يمكن أن يكون عنق زجاجة في سيناريوهات الحمل العالي بملايين الاستدعاءات في الثانية.

Autorelease Pool

Autorelease Pool هو آلية تحرير مؤجلة تُستخدم في Objective-C وبعض سيناريوهات Swift. تُوضع الكائنات في المجمع وتتلقى release عند تفريغ المجمع. في الحلقات التي تحتوي على العديد من الكائنات المؤقتة (مثل تحليل JSON)، يُقلل إنشاء autoreleasepool مخصص من الاستهلاك الأقصى للذاكرة.

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

كيف يختلف ARC عن إدارة الذاكرة اليدوية (MRR)؟

في الإدارة اليدوية (MRR)، كان المطور يستدعي صراحة retain و release و autorelease. ARC يُدخل هذه الاستدعاءات تلقائياً في مرحلة الترجمة، مما يلغي خطر double-free والتسربات بسبب نسيان release وأخطاء موازنة retain/release.

هل يمكن لـ ARC العمل مع كود C/C++؟

ARC يدير فقط كائنات Objective-C وفئات Swift. بالنسبة لهياكل ومؤشرات C/C++، لا يُطبق ARC — تُدار هذه الكائنات يدوياً أو عبر المؤشرات الذكية لـ C++ (shared_ptr, unique_ptr). كائنات Core Foundation (CFString, CGColor) أيضاً لا تغطيها ARC.

متى تستخدم weak ومتى تستخدم unowned؟

weak — عندما يمكن تحرير الكائن قبل الكائن المُشار إليه (المفوّضون، الإغلاقات غير المتزامنة). unowned — عندما يكون مضموناً أن الكائن يعيش على الأقل بقدر المُشار إليه (أب-ابن حيث لا يمكن للابن الوجود بدون الأب). إذا لم تكن متأكداً، اختر weak.

ما هي الأنواع الوجودية (Existential Types) وكيف تؤثر على ARC؟

الأنواع الوجودية (protocol كـنوع) في Swift تُغلّف القيمة في حاوية خاصة (حاوية وجودية). هذا يزيد عدد retain/release على حدود البروتوكولات. في Swift 5.7+، تُقلل أنواع النتائج غير الشفافة ومعاملات some الكلفة الإضافية بإزالة الحاوية.

كيف تتحقق من retain count في Swift؟

لا توجد واجهة برمجة تطبيقات مباشرة لقراءة retain count في Swift — فهي تعتبر تفصيل تنفيذي. للتشخيص، استخدم Instruments (Allocations, Leaks) أو مصحح الذاكرة في Xcode. تُظهر هذه الأدوات عدد نسخ الفئة الحية وسلاسل الاحتفاظ.

الخلاصة

  • ARC — نظام إدارة ذاكرة قائم على المترجم لـ Swift و Objective-C يعمل عبر عد المرجع
  • المبدأ — كل كائن له retain count؛ عند الوصول إلى الصفر، يُحرر الكائن فوراً وبشكل حتمي
  • الفرق عن GC — يعمل ARC بدون خلفية أو توقف Stop-The-World، لكنه يتطلب التحكم في retain cycles
  • Strong — يزيد العداد؛ weak و unowned لا يزيدانه، لكن unowned لا يُلغى عند التحرير
  • الإغلاقات — السبب الرئيسي لـ retain cycles في Swift؛ قائمة الالتقاط [weak self] هي الحل القياسي
  • Autorelease Pool — آلية تحرير مؤجلة للكائنات المؤقتة في الحلقات والسيناريوهات المخصصة
  • التشخيص — Xcode Memory Debugger و Instruments و LeakCanary (عبر جسر ObjC) لإيجاد المشاكل

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

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

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

اقرأ أيضًا