Delegate — یک الگوی طراحی است که در آن یک شیء اجرای وظیفه را به شیء دیگر تفویض میکند. در iOS این الگو از طریق پروتکلهای Swift و @protocol Objective-C پیادهسازی میشود. Delegation یکی از الگوهای بنیادین Cocoa Touch است که در UITableViewDelegate، UITextFieldDelegate و صدها API دیگر Apple استفاده میشود. طبق Apple Developer Documentation (2025)، حدود 70٪ از کلاسهای سیستمی UIKit از تفویضکنندگان برای سفارشیسازی رفتار بدون وراثت استفاده میکنند.
نکات اصلی
Delegate — یک الگوی طراحی رفتاری است که به یک شیء اجازه میدهد بخشی از وظایف خود را به شیء دیگر منتقل کند. بر خلاف وراثت، حيث کلاس فرزند متدهای والد را بازنویسی میکند، تفویضکننده از ترکیب استفاده میکند: شیء-مالک یک مرجع به تفویضکننده ذخیره میکند و متدهای آن را در نقاط مشخصی فراخوانی میکند.
تفویضکننده پروتکل را تعریف میکند — مجموعهای از متدها که تفویضکننده میتواند پیادهسازی کند. متدها به اجباری (required) و اختیاری (optional) تقسیم میشوند. در Swift متدهای اختیاری پروتکل با کلمه کلیدی @objc optional مشخص میشوند.
شیء A (مالک) دارای ویژگی delegate است — یک مرجع weak به شیء B (تفویضکننده). وقتی رویدادی رخ میدهد، A بررسی میکند که آیا B متد مربوط به پروتکل را پیادهسازی میکند و آن را فراخوانی میکند. مرجع weak الزامی است: بدون آن، تفویضکننده نمیتواند از حافظه آزاد شود، زیرا مالک آن را با یک مرجع قوی نگه میدارد.
protocol LoaderDelegate: AnyObject {
func loaderDidStart(_ loader: DataLoader)
func loader(_ loader: DataLoader, didLoad data: Data)
func loader(_ loader: DataLoader, didFailWith error: Error)
}
class DataLoader {
weak var delegate: LoaderDelegate?
func start() {
delegate?.loaderDidStart(self)
// بارگیری ناهمزمان
}
}کلاس DataLoader پروتکل LoaderDelegate را تعریف میکند و متدهای تفویضکننده را در نقاط کلیدی چرخه عمر بارگیری فراخوانی میکند. AnyObject تضمین میکند که پروتکل فقط توسط کلاسها قابل پیادهسازی است — این برای مرجع weak ضروری است.
پیادهسازی تفویضکننده در Swift شامل سه مرحله است: اعلام پروتکل، ایجاد ویژگی weak delegate در مالک و پیادهسازی پروتکل در کلاس-تفویضکننده. بیایید با مثال یک UITextField سفارشی با اعتبارسنجی بررسی کنیم.
protocol ValidatorDelegate: AnyObject {
func validate(_ input: String) -> Bool
func validatorDidFail(_ input: String)
}
class ValidatedTextField: UITextField {
weak var validator: ValidatorDelegate?
override func textDidChange() {
guard let text = self.text else { return }
if validator?.validate(text) == false {
validator?.validatorDidFail(text)
self.layer.borderColor = UIColor.red.cgColor
}
}
}
class LoginViewController: UIViewController, ValidatorDelegate {
let textField = ValidatedTextField()
override func viewDidLoad() {
super.viewDidLoad()
textField.validator = self
}
func validate(_ input: String) -> Bool {
return input.count >= 6
}
func validatorDidFail(_ input: String) {
print("اعتبارسنجی ناموفق: ورودی بسیار کوتاه است")
}
}LoginViewController پروتکل ValidatorDelegate را پیادهسازی میکند و خود را به عنوان تفویضکننده textField تعیین میکند. با هر تغییر متن، ValidatedTextField متد validate(_:) را فراخوانی میکند و اگر اعتبارسنجی ناموفق باشد — validatorDidFail(_:). Controller به عنوان واسط بین View و منطق اعتبارسنجی عمل میکند.
انتخاب بین تفویضکننده، اعلانها و بستهها به تعداد دریافتکنندگان و درجه وابستگی مؤلفهها بستگی دارد. هر مکانیزم مشکل ارتباط بین اشیاء را حل میکند، اما با مصالحههای متفاوت.
| ویژگی | Delegate | NotificationCenter | Closure |
|---|---|---|---|
| نوع ارتباط | ۱:۱ | ۱:N | ۱:۱ |
| وابستگی | ضعیف (از طریق پروتکل) | بسیار ضعیف (رشته-کلید) | متوسط (گرفتن زمینه) |
| ایمنی نوع | کامل | ندارد (Any?) | کامل |
| ریسک retain cycle | خیر (weak) | خیر | بله (گرفتن self) |
| زمان استفاده | فراخوانیهای پیچیده با چندین متد | رویدادهایی که بسیاری را علاقهمند میکند | بستههای ساده با ۱-۲ فراخوانی |
Delegate بهینه است وقتی باید یک سری رویدادهای مرتبط به یک دریافتکننده ارسال شود. NotificationCenter برای اعلانهای broadcast بهتر است. Closure — برای عملیاتهای ناهمزمان ساده، مانند completion handler در URLSession.
Objective-C از @protocol و @optional برای اعلام تفویضکنندگان استفاده میکند. بر خلاف Swift، تمام متدهای پروتکل به طور پیشفرض اختیاری هستند. تفاوت کلیدی — فراخوانی respondsToSelector: قبل از ارسال پیام به تفویضکننده، زیرا ممکن است متد پیادهسازی نشده باشد.
@protocol ImageCacheDelegate
@optional
- (void)cacheDidStartDownload: (ImageCache *)cache;
- (void)cache: (ImageCache *)cache didCacheImage: (UIImage *)image;
@required
- (void)cache: (ImageCache *)cache didFailWithError: (NSError *)error;
@end
@interface ImageCache : NSObject
@property (nonatomic, weak) id<ImageCacheDelegate> delegate;
- (void)downloadImageAtURL: (NSURL *)url;
@end
@implementation ImageCache
- (void)downloadImageAtURL: (NSURL *)url {
if ([self.delegate respondsToSelector:@selector(cacheDidStartDownload:)]) {
[self.delegate cacheDidStartDownload:self];
}
// بارگیری ناهمزمان تصویر
}
@endتفاوت کلیدی در Objective-C: قبل از فراخوانی متد اختیاری، بررسی respondsToSelector: ضروری است. در Swift متدهای اختیاری پروتکل این بررسی را حذف میکنند — optional chaining (?.) به طور خودکار حالت عدم پیادهسازی را مدیریت میکند.
خطاهای استفاده از تفویضکنندگان منجر به نشت حافظه، crashes برنامه و باگهای غیربدیهی میشود. بیایید پنج مشکل رایج را بررسی کنیم.
Retain cycle — رایجترین خطا. اگر ویژگی delegate به عنوان strong اعلام شود و تفویضکننده نیز به نوبه خود مالک شیء-مالک باشد، یک چرخه نگهداری تشکیل میشود. هر دو شیء هرگز از حافظه آزاد نخواهند شد. راهحل: همیشه تفویضکننده را به عنوان weak var در Swift یا @property (weak) در Objective-C اعلام کنید.
اگر شیء-مالک از تفویضکننده بیشتر عمر کند و مرجع باقی بماند، فراخوانی متد تفویضکننده منجر به EXC_BAD_ACCESS میشود. مرجع weak این مشکل را به طور خودکار حل میکند: پس از آزاد شدن تفویضکننده، ویژگی nil میشود. با این حال، در سناریوهای چندنخی بهتر است تفویضکننده را در نخ اصلی نیز بررسی کنید.
پروتکلی با بیش از ۲۰ متد اصل جداسازی رابطها (ISP) را نقض میکند. UITableViewDelegate حدود ۳۰ متد اختیاری دارد — این یک استثنای تاریخی است. در پروتکلهای خود بهتر است مسئولیت را به چند پروتکل کوچک تقسیم کنید، هر کدام با نقش خود.
APIهای سیستمی Apple به طور فعال از الگوی Delegate استفاده میکنند. بیایید سه مثال کلیدی از UIKit را بررسی کنیم که در هر برنامه iOS یافت میشوند.
| API | پروتکل | متدهای کلیدی |
|---|---|---|
| UITableView | UITableViewDelegate | didSelectRowAt, heightForRowAt, willDisplay |
| UITextField | UITextFieldDelegate | shouldChangeCharactersIn, didBeginEditing, shouldReturn |
| URLSession | URLSessionDelegate | didReceiveChallenge, didCompleteWithError, didBecomeInvalidWithError |
هر یک از این پروتکلها جنبههای مختلف رفتار را پیادهسازی میکنند: UITableViewDelegate ظاهر و واکنش به لمس را مدیریت میکند، UITextFieldDelegate ورود متن را کنترل میکند، URLSessionDelegate رویدادهای شبکه را پردازش میکند. این انعطافپذیری الگو را نشان میدهد: تفویضکننده میتواند با هر حوزه مسئولیتی سازگار شود.
سوالات متداول
Delegate رفتار و ظاهر را مدیریت میکند (ارتفاع سلول، واکنش به لمس). DataSource دادهها را فراهم میکند (تعداد ردیفها، محتوای سلولها). در UITableViewDelegate و UITableViewDataSource — این دو پروتکل جداگانه هستند که مسئولیت ارائه و داده را تقسیم میکنند.
مرجع weak از retain cycle جلوگیری میکند. مالک (مثلاً UITableView) مرجع را به تفویضکننده فقط به صورت weak نگه میدارد. اگر تفویضکننده (UIViewController) مالک جدول باشد، مرجع قوی به تفویضکننده یک چرخه ایجاد میکند: ViewController → UITableView → Delegate (ViewController). Weak این چرخه را میشکند.
در SwiftUI الگوی Delegate کمتر استفاده میشود — @Binding، @State و بستهها جایگزین آن میشوند. با این حال، delegate همچنان برای یکپارچهسازی UIKit از طریق UIViewRepresentable استفاده میشود. برای مثال، MKMapViewDelegate و WKUIDelegate هنگام بستهبندی مؤلفههای UIKit در SwiftUI همچنان مرتبط هستند.
@objc optional امکان اعلام متدهای اختیاری در پروتکل Swift را فراهم میکند. این یک مکانیسم سازگاری با Objective-C runtime است. بدون @objc همه متدهای پروتکل Swift به طور پیشفرض اجباری هستند. Optional در پروتکلهای UIKit استفاده میشود، جایی که تفویضکننده میتواند فقط متدهای مورد نیاز خود را پیادهسازی کند.
یک شیء میتواند فقط یک تفویضکننده برای هر ویژگی delegate داشته باشد. اگر نیاز به اطلاعرسانی به چندین شیء دارید، از multicast delegate، آرایهای از تفویضکنندگان یا NotificationCenter استفاده کنید. الگوی Delegate اساساً به عنوان یک رابطه ۱:۱ طراحی شده است.
خلاصه
ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد
IT Sectr از سال 2017 برنامههای iOS و Android را برای استارتاپها و کسبوکارها ایجاد میکند. ما به شما مشاوره میدهیم و بهترین راهحل را پیشنهاد خواهیم کرد.
همچنین بخوانید