ARC: چیست، اصل کار Automatic Reference Counting در iOS

نویسنده: IT Sectr منتشر شده: 2026-03-29 زمان مطالعه: 8 دقیقه

Automatic Reference Counting (ARC) — سیستم مدیریت حافظه در Swift و Objective-C است که به‌طور خودکار تعداد مراجع به هر شیء را شمارش کرده و پس از رسیدن شمارنده به صفر، آن را آزاد می‌کند. به استناد به Apple Swift Documentation, 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 تعداد باگ‌های کراش مربوط به حافظه را در برنامه‌های Apple تا 70% کاهش داد. توسعه‌دهندگان دیگر به صورت دستی retain/release را متوازن نمی‌کردند، که این کل طبقه‌ای از نشت‌ها و خطاهای double-free را برطرف کرد.

Automatic Reference Counting چگونه کار می‌کند

هر شیء در حافظه یک شمارنده مرجع (retain count) دارد. هنگام ایجاد شیء، شمارنده روی 1 قرار می‌گیرد. وقتی یک مرجع strong جدید به شیء اشاره می‌کند — شمارنده افزایش می‌یابد (retain). وقتی مرجع strong ناپدید می‌شود — شمارنده کاهش می‌یابد (release). با رسیدن به صفر، شیء بلافاصله دیلوکیت می‌شود.

کامپایلر Swift retain/release را در هر تنظیم قرار نمی‌دهد — از تحلیل استاتیک برای بهینه‌سازی استفاده می‌کند. به عنوان مثال، اگر شیء پس از انتقال استفاده نخواهد شد، کامپایلر می‌تواند release/retain ضایع را حذف کند. این بهینه‌سازی ARC Optimisation نامیده می‌شود.

swift
class Person {
    let name: String
    init(name: String) {
        self.name = name
        print("\(name) مقداردهی شد (retain count: 1)")
    }
    deinit {
        print("\(name) دیلوکیت شد")
    }
}

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 cycle و انتخاب صحیح weak/unowned را می‌طلبد. GC از این دغدغه‌ها آزاد می‌کند، اما به بهای مکافت‌های غیرتعیین‌پذیر و رشته اضافی.

Strong، Weak و Unowned: کلیدواژه‌های مرجع در ARC

ARC سه نوع کلیدواژه مرجع تعریف می‌کند، که هر کدام به‌طور متفاوت بر شمارنده و چرخه عمر شیء تأثیر می‌گذارد. انتخاب صحیح کلیدواژه اساس کار ایمن با حافظه در Swift است.

Strong

Strong — کلیدواژه پیش‌فرض. هر مرجع strong شمارنده retain شیء را 1 واحد افزایش می‌دهد. تا زمانی که حداقل یک مرجع strong وجود داشته باشد، شیء زنده است. تمام ویژگی‌های کلاس‌ها و متغیرهای محلی در Swift به‌طور پیش‌فرض strong هستند. مراجع strong یک رابطه مالکیت ایجاد می‌کنند: شیء A مالک شیء B است.

Weak

Weak — مرجعی که شمارنده retain را افزایش نمی‌دهد. شیء می‌تواند دیلوکیت شود، حتی اگر یک مرجع weak به آن اشاره کند. پس از دیلوکیشن، مرجع weak به‌طور خودکار به nil تنظیم می‌شود. مراجع weak همیشه به عنوان var با نوع اختیاری (?) اعلام می‌شوند. آنها برای شکستن چرخه retain استفاده می‌شوند، خصوصاً در الگوی delegate.

Unowned

Unowned — مرجع غیرمالک که مانند weak شمارنده retain را افزایش نمی‌دهد. اما مرجع unowned پس از دیلوکیشن به nil تنظیم نمی‌شود — دسترسی به شیء آزادشده باعث کراش می‌شود. Unowned زمانی استفاده می‌شود که تضمین شده باشد شیء حداقل به اندازه شیء مرجع‌دهنده عمر می‌کند. سناریو تیپیکی — بسته‌ها (closures) و روابط والد-فرزند با عمر تضمینی.

swift
class Customer {
    let name: String
    var card: CreditCard?         // strong
    init(name: String) { self.name = name }
    deinit { print("\(name) دیلوکیت شد") }
}

class CreditCard {
    let number: String
    unowned let customer: Customer   // unowned — مالک نیست
    init(number: String, customer: Customer) {
        self.number = number
        self.customer = customer
    }
    deinit { print("کارت \(number) دیلوکیت شد") }
}

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 nil شود، هر دو شیء آزاد می‌شوند — retain cycle ایجاد نمی‌شود. اگر card.customer strong بود، چرخه آزادسازی را مسدود می‌کرد.

مشکلات رایج ARC و راه‌حل‌ها

علیرغم آتوماتیک بودن، ARC پناسئه نیست. توسعه‌دهندگان با چند مشکل تیپیک مواجه می‌شوند که نیازمند درک مکانیسم داخلی مدیریت حافظه است.

Retain Cycle در بسته‌ها

بسته‌ها (closures) در Swift متغیرهای خارجی را از طریق مرجع strong برداشته می‌کنند. اگر بسته به یک ویژگی کلاس تنظیم شود و self را برداشته کند — retain cycle ایجاد می‌شود: کلاس بسته را، بسته self را نگه می‌دارد. راه‌حل — capture list با 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() { }
}

Capture list [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 — مکانیسم تأخیر release است که در Objective-C و برخی سناریوهای Swift استفاده می‌شود. شیئ‌ها در یک پول قرار می‌گیرند و هنگام drain پول، 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 — وقتی شیء می‌تواند زودتر از شیء مرجع‌دهنده دیلوکیت شود (delegates، بسته‌های ناهمگام). unowned — وقتی تضمین شده باشد شیء حداقل به اندازه شیء مرجع‌دهنده عمر می‌کند (والد-فرزند، که فرزند نمی‌تواند بدون والد وجود داشته باشد). اگر مطمئن نیستید — weak را انتخاب کنید.

انواع وجودی (اکزیستانشیال) چیستند و چگونه بر ARC تأثیر می‌گذارند؟

انواع اکزیستانشیال (protocol as type) در Swift مقدار را در یک کانتینر خاص (existential container) بسته‌بندی می‌کنند. این تعداد retain/release را در مرزهای پروتکل افزایش می‌دهد. در Swift 5.7+، opaque result types و پارامترهای some با حذف کانتینر، سرراهد را کاهش می‌دهند.

چگونه retain count را در Swift بررسی کنیم؟

برای خواندن retain count در Swift API مستقیمی وجود ندارد — این جزئیات پیاده‌سازی محسوب می‌شود. برای دیاگنوز از Instruments (Allocations, Leaks) یا Memory Debugger در Xcode استفاده کنید. این ابزارها تعداد نمونه‌های زنده کلاس و زنجیره نگهداری را نشان می‌دهند.

خلاصه

  • ARC — سیستم مدیریت حافظه کامپایلری Swift و Objective-C است که از طریق شمارش مراجع کار می‌کند
  • اصل — هر شیء retain count دارد؛ با صفر شدن، شیء بلافاصله و به‌صورت تعیین‌پذیر آزاد می‌شود
  • تفاوت با GC — ARC بدون رشته زمینه و مکافت Stop-The-World کار می‌کند، اما نیازمند کنترل retain cycle است
  • Strong — شمارنده را افزایش می‌دهد؛ weak و unowned افزایش نمی‌دهند، اما unowned در دیلوکیشن صفر نمی‌شود
  • بسته‌ها — علت اصلی retain cycle در Swift؛ capture list [weak self] راه‌حل استاندارد
  • Autorelease Pool — مکانیسم آزادسازی تأخیری برای شیئ‌های موقت در حلقه‌ها و سناریوهای سفارشی
  • دیاگنوز — Xcode Memory Debugger، Instruments و LeakCanary (از طریق ObjC bridge) برای یافتن مشکلات

ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد

IT Sectr از سال 2017 برنامه‌های iOS و Android را برای استارتاپ‌ها و کسب‌وکارها ایجاد می‌کند. ما به شما مشاوره می‌دهیم و بهترین راه‌حل را پیشنهاد خواهیم کرد.

بحث درباره پروژه

همچنین بخوانید