Delegate: این چیست، الگوی تفویض و چگونه روی iOS کار می‌کند

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

Delegate — یک الگوی طراحی است که در آن یک شیء اجرای وظایف را از طریق پروتکلی با متدهای از پیش تعریف‌شده به شیء دیگر واگذار می‌کند. در توسعه iOS، Delegate یکی از الگوهای بنیادین Cocoa Touch است که برای اعلان ناهمگام بدون ارتباط مستقیم بین فرستنده و گیرنده استفاده می‌شود. بر اساس Apple Documentation (2025)، تفویض در Foundation و UIKit برای پردازش رویدادهای جدول، درخواست‌های شبکه و مدیریت موقعیت مکانی به کار می‌رود. این الگو اتصال ضعیف بین مؤلفه‌ها و استفاده مجدد از کد را تضمین می‌کند.

نکات اصلی

  • Delegate — الگویی که در آن شیء پردازش رویدادها را از طریق پروتکل به شیء دیگر واگذار می‌کند.
  • Protocol در Swift مجموعه متدهایی را تعریف می‌کند که delegate باید یا می‌تواند پیاده‌سازی کند.
  • Weak reference برای ویژگی delegate به منظور جلوگیری از retain cycle الزامی است.
  • @objc optional امکان اختیاری کردن متدهای پروتکل را فراهم می‌کند.
  • URLSessionDelegate — delegate ناهمگام برای پردازش رویدادهای درخواست شبکه.

Delegate چیست؟

Delegate (نماینده) — شیءای است که پروتکل مشخصی را پیاده‌سازی می‌کند و اعلان‌هایی درباره رویدادهای شیء دیگر دریافت می‌کند. الگوی Delegation جایگزینی برای وراثت است: به جای ایجاد زیرکلاس برای بازنویسی متدها، شیء پردازش رویدادها را به یک شیء خارجی واگذار می‌کند. در iOS، delegation از طریق پروتکل‌های Swift با متدهای اجباری و اختیاری پیاده‌سازی می‌شود. ویژگی delegate همیشه به عنوان weak var اعلام می‌شود تا از ارجاعات چرخشی بین اشیاء جلوگیری شود.

تعریف پروتکل Delegate

پروتکل delegate قرارداد تعامل بین اشیاء را تعریف می‌کند. متدهای اجباری باید توسط نماینده پیاده‌سازی شوند، در غیر این صورت کد کامپایل نخواهد شد. متدهای اختیاری با ویژگی @objc optional مشخص می‌شوند و به نماینده اجازه می‌دهند فقط به رویدادهای مورد نیاز واکنش نشان دهد. نام متدها از قرارداد پیروی می‌کنند: پارامتر اول — شیء فرستنده، دوم — داده رویداد. به عنوان مثال، tableView(_:didSelectRowAt:) نشان می‌دهد که فرستنده UITableView است و داده اندیس ردیف انتخاب‌شده است.

swift
// پروتکل Delegate
protocol DownloadManagerDelegate: AnyObject {
    func downloadManager(_ manager: DownloadManager,
                          didFinishWith data: Data)
    func downloadManager(_ manager: DownloadManager,
                          didFailWith error: Error)

    @objc optional func downloadManager(_ manager: DownloadManager,
                                didUpdateProgress progress: Float)
}

// کلاس استفاده‌کننده از Delegate
class DownloadManager {
    weak var delegate: DownloadManagerDelegate?

    func startDownload(from url: URL) {
        URLSession.shared.dataTask(with: url) { [weak self] data, _, error in
            guard let self else { return }
            if let error = error {
                self.delegate?.downloadManager(self, didFailWith: error)
            } else if let data = data {
                self.delegate?.downloadManager(self, didFinishWith: data)
            }
        }.resume()
    }
}

ارجاع ضعیف به delegate

ویژگی delegate لزوماً به عنوان weak var (اشاره‌گر ضعیف) برای جلوگیری از retain cycle اعلام می‌شود. اگر ارجاع قوی بود، نماینده و شیء واگذارکننده یکدیگر را نگه می‌داشتند و ARC نمی‌توانست حافظه آن‌ها را آزاد کند. پروتکل‌های delegate از AnyObject (فقط برای کلاس‌ها) ارث‌بری می‌کنند که استفاده از weak را ممکن می‌سازد. structها و enumها به دلیل معناشناسی مقادیر نمی‌توانند نماینده باشند. جایگزین برای value types — callback closureها.

swift
class ViewController: DownloadManagerDelegate {
    let manager = DownloadManager()

    override func viewDidLoad() {
        super.viewDidLoad()
        manager.delegate = self // weak — بدون retain cycle
        manager.startDownload(from: url)
    }

    func downloadManager(_ manager: DownloadManager, didFinishWith data: Data) {
        processData(data)
    }

    func downloadManager(_ manager: DownloadManager, didFailWith error: Error) {
        showError(error)
    }
}

الگوی Delegate در iOS چگونه کار می‌کند؟

الگوی Delegate بر اساس اصل «یک-به-یک» کار می‌کند: یک شیء فرستنده می‌تواند در هر لحظه فقط یک نماینده داشته باشد. وقتی رویدادی رخ می‌دهد، فرستنده بررسی می‌کند که delegate تنظیم شده است یا خیر و متد مربوط به پروتکل را فراخوانی می‌کند. مزیت نسبت به فراخوانی مستقیم — فرستنده نوع نماینده را نمی‌داند، فقط می‌داند که با پروتکل مطابقت دارد. این با اصل وارونگی وابستگی (DIP) از SOLID مطابقت دارد.

چرخه زندگی نماینده

نماینده از طریق انتساب تنظیم می‌شود: someObject.delegate = self. هنگام تخصیص‌زدایی نماینده، ویژگی به دلیل معناشناسی weak به طور خودکار nil می‌شود. قبل از فراخوانی متد delegate از طریق optional chaining بررسی می‌شود: delegate?.method(). اگر delegate nil باشد — فراخوانی بدون کرش نادیده گرفته می‌شود. برای متدهای اختیاری پروتکل از بررسی اضافی استفاده می‌شود: delegate?.responds(to: #selector(...))، اگرچه در Swift این بررسی معمولاً از طریق اعلام متد اختیاری به صورت ضمنی انجام می‌شود.

اعلان ناهمگام از طریق Delegate

در محیط چندنخی، delegate برای بازگشت ناهمگام نتیجه استفاده می‌شود. URLSession URLSessionDelegate را با متدهایی که هنگام دریافت داده، وقفه یا خطای احراز هویت فراخوانی می‌شوند ارائه می‌دهد. متدهای delegate در صف پس‌زمینه URLSession اجرا می‌شوند، بنابراین برای به‌روزرسانی‌های UI نیاز به ارسال به main queue است. delegate ناهمگام نخ فراخوان را مسدود نمی‌کند و امکان ادامه سایر وظایف را فراهم می‌کند.

swift
class NetworkService: NSObject, URLSessionDataDelegate {
    private lazy var session = URLSession(
        configuration: .default,
        delegate: self,
        delegateQueue: OperationQueue()
    )
    private var receivedData = Data()

    func urlSession(_ session: URLSession,
                    dataTask: URLSessionDataTask,
                    didReceive data: Data) {
        receivedData.append(data)
        let progress = Float(receivedData.count) / Float(expectedSize)
        DispatchQueue.main.async {
            self.progressHandler?(progress)
        }
    }

    func urlSession(_ session: URLSession,
                    task: URLSessionTask,
                    didCompleteWithError error: Error?) {
        if let error = error {
            delegate?.networkService(self, didFailWith: error)
        } else {
            delegate?.networkService(self, didReceive: receivedData)
        }
    }
}

Delegate vs Callback: مقایسه رویکردها

Delegate و Callback یک وظیفه — اعلان ناهمگام — را حل می‌کنند، اما به روش‌های مختلف. Delegate از پروتکل با متدهای نام‌گذاری‌شده استفاده می‌کند، callback — از closure با ضبط زمینه. انتخاب به تعداد رویدادها، پیچیدگی امضاها و ترجیحات معماری بستگی دارد. Apple برای APIهای با رویدادهای متعدد (UITableView — 20+ متد) delegate و برای تکمیل‌های یک‌باره callback را توصیه می‌کند.

زمانی که Delegate برتر است

Delegate زمانی ترجیح داده می‌شود که نیاز به پردازش چندین رویداد مختلف از یک منبع باشد. به عنوان مثال، CLLocationManager به نماینده درباره تغییر مکان، خطاهای مجوز، ورود/خروج از مناطق جغرافیایی و تغییر وضعیت سرویس‌ها اطلاع می‌دهد. هر رویداد یک متد مجزای پروتکل با نام واضح و پارامترهای تایپ‌شده است. Delegate همچنین برای پیکربندی رفتار (متدهای should, will, did) مناسب است.

زمانی که Callback برتر است

Callback برای درخواست‌های یک‌باره با یک نتیجه ساده‌تر است. Completion handler در URLSession.dataTask یک خط در نقطه فراخوانی در مقابل حداقل سه متد پروتکل اشغال می‌کند. Callback همچنین برای زنجیره‌های تابعی طبیعی‌تر است (map, flatMap, async/await). با این حال، در تو در تو شدن بیش از 2-3 سطح، callback به Callback Hell تبدیل می‌شود، در حالی که delegate همیشه مسطح باقی می‌ماند.

Delegat‌های داخلی در iOS SDK

iOS SDK شامل ده‌ها پروتکل delegate داخلی برای زیرسیستم‌های مختلف است. هر یک از آنها برای یک سناریوی تعامل خاص طراحی شده است. بر اساس Apple Documentation (2025)، پرکاربردترین delegateها عبارتند از UITableViewDelegate، UITextFieldDelegate، CLLocationManagerDelegate، URLSessionDelegate و UNUserNotificationCenterDelegate. این پروتکل‌ها بین 3 تا 30 متد با درجات مختلف الزام دارند.

UITableViewDelegate

UITableViewDelegate ظاهر و رفتار سلول‌های جدول را مدیریت می‌کند. شامل متدهایی برای پردازش انتخاب ردیف، تنظیم ارتفاع سلول‌ها، نمایش‌های سفارشی header/footer و اقدامات کشیدن است. تمام متدهای پروتکل اختیاری هستند که امکان پیاده‌سازی فقط عملکرد مورد نیاز را فراهم می‌کند. در غیاب نماینده، جدول با تنظیمات پیش‌فرض کار می‌کند. از نظر تاریخی، delegate با UITableViewDataSource ترکیب می‌شد.

URLSessionDelegate

URLSessionDelegate کنترل دقیق بر درخواست‌های HTTP را فراهم می‌کند. متدهای نماینده هنگام دریافت پاسخ سرور، داده‌ها، اتمام بارگیری فراخوانی می‌شوند. زیرپروتکل‌های تخصصی URLSessionTaskDelegate و URLSessionDataDelegate عملکرد پایه را برای انواع خاص وظایف گسترش می‌دهند. Delegate برای پشتیبانی از بارگیری‌های پس‌زمینه، گواهی‌های SSL و پردازش سفارشی تغییر مسیرها مورد نیاز است.

Delegateتعداد متدکاربرد
UITableViewDelegate25ظاهر و تعامل با جدول
UITextFieldDelegate8پردازش ورود متن و صفحه‌کلید
CLLocationManagerDelegate12به‌روزرسانی موقعیت و مناطق جغرافیایی
URLSessionDelegate6مدیریت جلسه HTTP و گواهی‌ها
UNUserNotificationCenterDelegate4پردازش اعلان‌های push در پیش‌زمینه

مدیریت حافظه هنگام کار با Delegate

مدیریت حافظه یک جنبه حیاتی کار با delegate در iOS است. ARC (Automatic Reference Counting) به طور خودکار حافظه را مدیریت می‌کند، اما فقط با استفاده صحیح از ارجاعات weak/unowned. نقض قوانین منجر به نشت حافظه یا تخصیص‌زدایی زودهنگام می‌شود. Delegate که به عنوان strong اعلام شده است، اگر صاحب نماینده نیز به شیء واگذارکننده ارجاع داشته باشد، retain cycle ایجاد می‌کند.

Retain cycle از طریق Delegate

Retain cycle زمانی رخ می‌دهد که شیء A (صاحب) خود را به عنوان نماینده شیء B تنظیم می‌کند و B یک ارجاع قوی به delegate ذخیره می‌کند. مثال: ViewController یک URLSession ایجاد می‌کند، self را به عنوان نماینده جلسه تنظیم می‌کند، اما URLSession به طور پیش‌فرض ارجاع قوی به delegate ذخیره می‌کند، اگر delegateQueue مشخص نشده باشد. راه‌حل — همیشه مستندات API را برای نوع ارجاع به delegate (weak یا strong) بررسی کنید و به صراحت delegate را در deinit صفر کنید.

swift
class SafeViewController: UIViewController {
    private var session: URLSession?
    private var service: NetworkService?

    override func viewDidLoad() {
        super.viewDidLoad()
        service = NetworkService()
        service?.delegate = self
    }

    deinit {
        // صفر کردن delegate در deinit — best practice
        service?.delegate = nil
        session?.invalidateAndCancel()
    }
}

// URLSession با weak delegate از طریق NSObject
class WeakDelegateSession: NSObject {
    private weak var delegate: URLSessionDelegate?

    func createSession() -> URLSession {
        let queue = OperationQueue()
        queue.maxConcurrentOperationCount = 1
        return URLSession(
            configuration: .default,
            delegate: self,
            delegateQueue: queue
        )
    }
}

بررسی ایمن delegate قبل از فراخوانی

قبل از فراخوانی متد نماینده، باید بررسی شود که نماینده وجود دارد (nil نیست) و متد فراخوانی‌شده را پیاده‌سازی می‌کند. برای متدهای اجباری پروتکل بررسی لازم نیست — کامپایلر پیاده‌سازی را تضمین می‌کند. برای متدهای اختیاری از respond(to:) یا optional chaining استفاده کنید. اگر نماینده تخصیص‌زدایی شده باشد، ارجاع weak به طور خودکار nil می‌شود و فراخوانی نماینده نادیده گرفته می‌شود. این رفتار ایمنی است که نیاز به پردازش اضافی ندارد.

خطاهای رایج در پیاده‌سازی Delegate

توسعه‌دهندگان اغلب هنگام کار با الگوی Delegate، به ویژه در مراحل اولیه یادگیری iOS، اشتباه می‌کنند. رایج‌ترین: retain cycle به دلیل strong delegate، فراموش کردن فراخوانی delegate?.method()، امضای نادرست متدهای پروتکل، تنظیم delegate پس از شروع عملیات و برخوردهای چندنخی. بیایید هر خطا و روش‌های پیشگیری از آن را بررسی کنیم.

Strong reference به جای weak

بحرانی‌ترین خطا — اعلام ویژگی delegate به عنوان strong var به جای weak var. این یک retain cycle ایجاد می‌کند که در آن نه نماینده و نه شیء واگذارکننده نمی‌توانند آزاد شوند. پیامدها: نشت حافظه، کندی برنامه و باگ‌های پنهان. راه‌حل: همیشه از weak var برای delegate استفاده کنید و پروتکل را از AnyObject ارث‌بری کنید تا استفاده از value types به عنوان نماینده حذف شود.

تنظیم delegate پس از شروع عملیات

اگر delegate پس از فراخوانی متد ناهمگام تنظیم شود، اولین رویدادها ممکن است از دست بروند. مثال: فراخوانی startDownload() قبل از انتساب manager.delegate = self منجر به از دست دادن callback تکمیل می‌شود، اگر بارگیری به صورت همگام یا بسیار سریع انجام شود. راه‌حل: قبل از فراخوانی متد ناهمگام، delegate را تنظیم کنید و ترتیب مقداردهی اولیه را در نظرات پروتکل مستند کنید.

سوالات متداول

چرا delegate به صورت weak اعلام می‌شود؟

Weak از retain cycle بین نماینده و شیء واگذارکننده جلوگیری می‌کند. اگر ارجاع strong بود، اشیاء یکدیگر را نگه می‌داشتند و ARC نمی‌توانست آن‌ها را آزاد کند. ارجاع weak به طور خودکار هنگام تخصیص‌زدایی نماینده nil می‌شود. این یک روش استاندارد Cocoa Touch از زمان ظهور Objective-C است و برای سازگاری معکوس در Swift حفظ شده است.

تفاوت بین delegate و dataSource چیست؟

Delegate رویدادها را پردازش می‌کند و رفتار را مدیریت می‌کند (ارتفاع سلول‌ها، واکنش به کلیک‌ها). DataSource داده‌ها را برای نمایش فراهم می‌کند (تعداد ردیف‌ها، سلول‌ها). Delegate به سوال «چگونه؟» پاسخ می‌دهد، dataSource — به سوال «چه؟». در iOS هر دو از طریق پروتکل‌ها پیاده‌سازی می‌شوند، اغلب در یک کنترلر، اما از نظر مفهومی جدا هستند.

آیا می‌توان از struct به عنوان delegate استفاده کرد؟

خیر، اگر پروتکل از AnyObject (پروتکل کلاسی) ارث‌بری کند. ارجاعات weak فقط برای reference types (کلاس‌ها) در دسترس هستند. برای value types (struct, enum) از callback closureها یا یک کلاس wrapper جداگانه استفاده کنید. اگر پروتکل را کنترل می‌کنید، می‌توانید از AnyObject ارث‌بری نکنید، اما در آن صورت weak ممنوع است — بین delegate ضعیف و struct-نماینده آگاهانه انتخاب کنید.

متد responds(to:) چیست و چه کاربردی دارد؟

responds(to:) — متد NSObjectProtocol است که بررسی می‌کند آیا شیء selector مشخص‌شده را پیاده‌سازی می‌کند یا خیر. برای بررسی متدهای اختیاری @objc پروتکل قبل از فراخوانی استفاده می‌شود. بدون این بررسی، فراخوانی یک متد اختیاری پیاده‌سازی‌نشده منجر به NSInvalidArgumentException می‌شود. در Swift برای پروتکل‌های با @objc optional، بررسی می‌تواند از طریق optional binding ضمنی باشد.

آیا Delegate singleton است؟

خیر، delegate یک الگوی تفویض است، نه singleton. بر خلاف singleton، نماینده می‌تواند در زمان اجرا جایگزین شود و برای هر شیء واگذارکننده در یک نمونه وجود دارد. یک شیء می‌تواند نماینده چندین فرستنده باشد. Singleton یک الگوی ایجاد است که یک نمونه واحد از کلاس را تضمین می‌کند و ربطی به تفویض ندارد.

خلاصه

  • Delegate — الگویی که در آن شیء پردازش رویدادها را از طریق پروتکلی با متدهای تایپ‌شده به شیء دیگر واگذار می‌کند.
  • Weak var برای ویژگی delegate به منظور جلوگیری از retain cycle و نشت حافظه الزامی است.
  • پروتکل متدهای اجباری و اختیاری (@objc optional) تفویض را تعریف می‌کند.
  • iOS SDK شامل 15+ پروتکل delegate داخلی است: UITableViewDelegate، URLSessionDelegate، CLLocationManagerDelegate.
  • Delegate هنگام 3+ رویداد مختلف از یک منبع بر callback برتری دارد (CLLocationManager).
  • Delegate ناهمگام در URLSession برای دریافت داده و پیشرفت بدون مسدود کردن نخ استفاده می‌شود.
  • Delegate را قبل از شروع عملیات ناهمگام تنظیم کنید و برای کار ایمن با حافظه در deinit صفر کنید.

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

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

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

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