Delegate — یک الگوی طراحی است که در آن یک شیء اجرای وظایف را از طریق پروتکلی با متدهای از پیش تعریفشده به شیء دیگر واگذار میکند. در توسعه iOS، Delegate یکی از الگوهای بنیادین Cocoa Touch است که برای اعلان ناهمگام بدون ارتباط مستقیم بین فرستنده و گیرنده استفاده میشود. بر اساس Apple Documentation (2025)، تفویض در Foundation و UIKit برای پردازش رویدادهای جدول، درخواستهای شبکه و مدیریت موقعیت مکانی به کار میرود. این الگو اتصال ضعیف بین مؤلفهها و استفاده مجدد از کد را تضمین میکند.
نکات اصلی
Delegate (نماینده) — شیءای است که پروتکل مشخصی را پیادهسازی میکند و اعلانهایی درباره رویدادهای شیء دیگر دریافت میکند. الگوی Delegation جایگزینی برای وراثت است: به جای ایجاد زیرکلاس برای بازنویسی متدها، شیء پردازش رویدادها را به یک شیء خارجی واگذار میکند. در iOS، delegation از طریق پروتکلهای Swift با متدهای اجباری و اختیاری پیادهسازی میشود. ویژگی delegate همیشه به عنوان weak var اعلام میشود تا از ارجاعات چرخشی بین اشیاء جلوگیری شود.
پروتکل delegate قرارداد تعامل بین اشیاء را تعریف میکند. متدهای اجباری باید توسط نماینده پیادهسازی شوند، در غیر این صورت کد کامپایل نخواهد شد. متدهای اختیاری با ویژگی @objc optional مشخص میشوند و به نماینده اجازه میدهند فقط به رویدادهای مورد نیاز واکنش نشان دهد. نام متدها از قرارداد پیروی میکنند: پارامتر اول — شیء فرستنده، دوم — داده رویداد. به عنوان مثال، tableView(_:didSelectRowAt:) نشان میدهد که فرستنده UITableView است و داده اندیس ردیف انتخابشده است.
// پروتکل 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 لزوماً به عنوان weak var (اشارهگر ضعیف) برای جلوگیری از retain cycle اعلام میشود. اگر ارجاع قوی بود، نماینده و شیء واگذارکننده یکدیگر را نگه میداشتند و ARC نمیتوانست حافظه آنها را آزاد کند. پروتکلهای delegate از AnyObject (فقط برای کلاسها) ارثبری میکنند که استفاده از weak را ممکن میسازد. structها و enumها به دلیل معناشناسی مقادیر نمیتوانند نماینده باشند. جایگزین برای value types — callback closureها.
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 بر اساس اصل «یک-به-یک» کار میکند: یک شیء فرستنده میتواند در هر لحظه فقط یک نماینده داشته باشد. وقتی رویدادی رخ میدهد، فرستنده بررسی میکند که delegate تنظیم شده است یا خیر و متد مربوط به پروتکل را فراخوانی میکند. مزیت نسبت به فراخوانی مستقیم — فرستنده نوع نماینده را نمیداند، فقط میداند که با پروتکل مطابقت دارد. این با اصل وارونگی وابستگی (DIP) از SOLID مطابقت دارد.
نماینده از طریق انتساب تنظیم میشود: someObject.delegate = self. هنگام تخصیصزدایی نماینده، ویژگی به دلیل معناشناسی weak به طور خودکار nil میشود. قبل از فراخوانی متد delegate از طریق optional chaining بررسی میشود: delegate?.method(). اگر delegate nil باشد — فراخوانی بدون کرش نادیده گرفته میشود. برای متدهای اختیاری پروتکل از بررسی اضافی استفاده میشود: delegate?.responds(to: #selector(...))، اگرچه در Swift این بررسی معمولاً از طریق اعلام متد اختیاری به صورت ضمنی انجام میشود.
در محیط چندنخی، delegate برای بازگشت ناهمگام نتیجه استفاده میشود. URLSession URLSessionDelegate را با متدهایی که هنگام دریافت داده، وقفه یا خطای احراز هویت فراخوانی میشوند ارائه میدهد. متدهای delegate در صف پسزمینه URLSession اجرا میشوند، بنابراین برای بهروزرسانیهای UI نیاز به ارسال به main queue است. delegate ناهمگام نخ فراخوان را مسدود نمیکند و امکان ادامه سایر وظایف را فراهم میکند.
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 و Callback یک وظیفه — اعلان ناهمگام — را حل میکنند، اما به روشهای مختلف. Delegate از پروتکل با متدهای نامگذاریشده استفاده میکند، callback — از closure با ضبط زمینه. انتخاب به تعداد رویدادها، پیچیدگی امضاها و ترجیحات معماری بستگی دارد. Apple برای APIهای با رویدادهای متعدد (UITableView — 20+ متد) delegate و برای تکمیلهای یکباره callback را توصیه میکند.
Delegate زمانی ترجیح داده میشود که نیاز به پردازش چندین رویداد مختلف از یک منبع باشد. به عنوان مثال، CLLocationManager به نماینده درباره تغییر مکان، خطاهای مجوز، ورود/خروج از مناطق جغرافیایی و تغییر وضعیت سرویسها اطلاع میدهد. هر رویداد یک متد مجزای پروتکل با نام واضح و پارامترهای تایپشده است. Delegate همچنین برای پیکربندی رفتار (متدهای should, will, did) مناسب است.
Callback برای درخواستهای یکباره با یک نتیجه سادهتر است. Completion handler در URLSession.dataTask یک خط در نقطه فراخوانی در مقابل حداقل سه متد پروتکل اشغال میکند. Callback همچنین برای زنجیرههای تابعی طبیعیتر است (map, flatMap, async/await). با این حال، در تو در تو شدن بیش از 2-3 سطح، callback به Callback Hell تبدیل میشود، در حالی که delegate همیشه مسطح باقی میماند.
iOS SDK شامل دهها پروتکل delegate داخلی برای زیرسیستمهای مختلف است. هر یک از آنها برای یک سناریوی تعامل خاص طراحی شده است. بر اساس Apple Documentation (2025)، پرکاربردترین delegateها عبارتند از UITableViewDelegate، UITextFieldDelegate، CLLocationManagerDelegate، URLSessionDelegate و UNUserNotificationCenterDelegate. این پروتکلها بین 3 تا 30 متد با درجات مختلف الزام دارند.
UITableViewDelegate ظاهر و رفتار سلولهای جدول را مدیریت میکند. شامل متدهایی برای پردازش انتخاب ردیف، تنظیم ارتفاع سلولها، نمایشهای سفارشی header/footer و اقدامات کشیدن است. تمام متدهای پروتکل اختیاری هستند که امکان پیادهسازی فقط عملکرد مورد نیاز را فراهم میکند. در غیاب نماینده، جدول با تنظیمات پیشفرض کار میکند. از نظر تاریخی، delegate با UITableViewDataSource ترکیب میشد.
URLSessionDelegate کنترل دقیق بر درخواستهای HTTP را فراهم میکند. متدهای نماینده هنگام دریافت پاسخ سرور، دادهها، اتمام بارگیری فراخوانی میشوند. زیرپروتکلهای تخصصی URLSessionTaskDelegate و URLSessionDataDelegate عملکرد پایه را برای انواع خاص وظایف گسترش میدهند. Delegate برای پشتیبانی از بارگیریهای پسزمینه، گواهیهای SSL و پردازش سفارشی تغییر مسیرها مورد نیاز است.
| Delegate | تعداد متد | کاربرد |
|---|---|---|
| UITableViewDelegate | 25 | ظاهر و تعامل با جدول |
| UITextFieldDelegate | 8 | پردازش ورود متن و صفحهکلید |
| CLLocationManagerDelegate | 12 | بهروزرسانی موقعیت و مناطق جغرافیایی |
| URLSessionDelegate | 6 | مدیریت جلسه HTTP و گواهیها |
| UNUserNotificationCenterDelegate | 4 | پردازش اعلانهای push در پیشزمینه |
مدیریت حافظه یک جنبه حیاتی کار با delegate در iOS است. ARC (Automatic Reference Counting) به طور خودکار حافظه را مدیریت میکند، اما فقط با استفاده صحیح از ارجاعات weak/unowned. نقض قوانین منجر به نشت حافظه یا تخصیصزدایی زودهنگام میشود. Delegate که به عنوان strong اعلام شده است، اگر صاحب نماینده نیز به شیء واگذارکننده ارجاع داشته باشد، retain cycle ایجاد میکند.
Retain cycle زمانی رخ میدهد که شیء A (صاحب) خود را به عنوان نماینده شیء B تنظیم میکند و B یک ارجاع قوی به delegate ذخیره میکند. مثال: ViewController یک URLSession ایجاد میکند، self را به عنوان نماینده جلسه تنظیم میکند، اما URLSession به طور پیشفرض ارجاع قوی به delegate ذخیره میکند، اگر delegateQueue مشخص نشده باشد. راهحل — همیشه مستندات API را برای نوع ارجاع به delegate (weak یا strong) بررسی کنید و به صراحت delegate را در deinit صفر کنید.
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
)
}
}
قبل از فراخوانی متد نماینده، باید بررسی شود که نماینده وجود دارد (nil نیست) و متد فراخوانیشده را پیادهسازی میکند. برای متدهای اجباری پروتکل بررسی لازم نیست — کامپایلر پیادهسازی را تضمین میکند. برای متدهای اختیاری از respond(to:) یا optional chaining استفاده کنید. اگر نماینده تخصیصزدایی شده باشد، ارجاع weak به طور خودکار nil میشود و فراخوانی نماینده نادیده گرفته میشود. این رفتار ایمنی است که نیاز به پردازش اضافی ندارد.
توسعهدهندگان اغلب هنگام کار با الگوی Delegate، به ویژه در مراحل اولیه یادگیری iOS، اشتباه میکنند. رایجترین: retain cycle به دلیل strong delegate، فراموش کردن فراخوانی delegate?.method()، امضای نادرست متدهای پروتکل، تنظیم delegate پس از شروع عملیات و برخوردهای چندنخی. بیایید هر خطا و روشهای پیشگیری از آن را بررسی کنیم.
بحرانیترین خطا — اعلام ویژگی delegate به عنوان strong var به جای weak var. این یک retain cycle ایجاد میکند که در آن نه نماینده و نه شیء واگذارکننده نمیتوانند آزاد شوند. پیامدها: نشت حافظه، کندی برنامه و باگهای پنهان. راهحل: همیشه از weak var برای delegate استفاده کنید و پروتکل را از AnyObject ارثبری کنید تا استفاده از value types به عنوان نماینده حذف شود.
اگر delegate پس از فراخوانی متد ناهمگام تنظیم شود، اولین رویدادها ممکن است از دست بروند. مثال: فراخوانی startDownload() قبل از انتساب manager.delegate = self منجر به از دست دادن callback تکمیل میشود، اگر بارگیری به صورت همگام یا بسیار سریع انجام شود. راهحل: قبل از فراخوانی متد ناهمگام، delegate را تنظیم کنید و ترتیب مقداردهی اولیه را در نظرات پروتکل مستند کنید.
سوالات متداول
Weak از retain cycle بین نماینده و شیء واگذارکننده جلوگیری میکند. اگر ارجاع strong بود، اشیاء یکدیگر را نگه میداشتند و ARC نمیتوانست آنها را آزاد کند. ارجاع weak به طور خودکار هنگام تخصیصزدایی نماینده nil میشود. این یک روش استاندارد Cocoa Touch از زمان ظهور Objective-C است و برای سازگاری معکوس در Swift حفظ شده است.
Delegate رویدادها را پردازش میکند و رفتار را مدیریت میکند (ارتفاع سلولها، واکنش به کلیکها). DataSource دادهها را برای نمایش فراهم میکند (تعداد ردیفها، سلولها). Delegate به سوال «چگونه؟» پاسخ میدهد، dataSource — به سوال «چه؟». در iOS هر دو از طریق پروتکلها پیادهسازی میشوند، اغلب در یک کنترلر، اما از نظر مفهومی جدا هستند.
خیر، اگر پروتکل از AnyObject (پروتکل کلاسی) ارثبری کند. ارجاعات weak فقط برای reference types (کلاسها) در دسترس هستند. برای value types (struct, enum) از callback closureها یا یک کلاس wrapper جداگانه استفاده کنید. اگر پروتکل را کنترل میکنید، میتوانید از AnyObject ارثبری نکنید، اما در آن صورت weak ممنوع است — بین delegate ضعیف و struct-نماینده آگاهانه انتخاب کنید.
responds(to:) — متد NSObjectProtocol است که بررسی میکند آیا شیء selector مشخصشده را پیادهسازی میکند یا خیر. برای بررسی متدهای اختیاری @objc پروتکل قبل از فراخوانی استفاده میشود. بدون این بررسی، فراخوانی یک متد اختیاری پیادهسازینشده منجر به NSInvalidArgumentException میشود. در Swift برای پروتکلهای با @objc optional، بررسی میتواند از طریق optional binding ضمنی باشد.
خیر، delegate یک الگوی تفویض است، نه singleton. بر خلاف singleton، نماینده میتواند در زمان اجرا جایگزین شود و برای هر شیء واگذارکننده در یک نمونه وجود دارد. یک شیء میتواند نماینده چندین فرستنده باشد. Singleton یک الگوی ایجاد است که یک نمونه واحد از کلاس را تضمین میکند و ربطی به تفویض ندارد.
خلاصه
ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد
IT Sectr از سال 2017 برنامههای iOS و Android را برای استارتاپها و کسبوکارها ایجاد میکند. ما به شما مشاوره میدهیم و بهترین راهحل را پیشنهاد خواهیم کرد.
همچنین بخوانید