Delegate: ما هو، نمط التفويض وبروتوكولات iOS

المؤلف: IT Sectr نُشر: 2026-02-17 وقت القراءة: 9 دق

Delegate هو نمط تصميم حيث يفوض كائن تنفيذ مهمة إلى كائن آخر. في iOS، يتم تنفيذ النمط من خلال بروتوكولات Swift و @protocol Objective-C. التفويض هو أحد الأنماط الأساسية لـ Cocoa Touch، المستخدم في UITableViewDelegate و UITextFieldDelegate ومئات من واجهات Apple الأخرى. وفقًا لوثائق مطوري Apple (2025)، حوالي 70% من فئات النظام UIKit تستخدم المفوضين لتخصيص السلوك دون وراثة.

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

  • Delegate — نمط حيث يفوض الكائن A تنفيذ مهمة إلى الكائن B من خلال بروتوكول أو واجهة
  • بروتوكول Swift يحدد مجموعة من الأساليب التي يمكن أو يجب على المفوض تنفيذها
  • مرجع ضعيف للمفوض إلزامي لمنع دورات الاحتفاظ وتسريبات الذاكرة
  • UITableViewDelegate — المثال الأكثر شيوعًا للمفوض في iOS مع أكثر من 20 طريقة اختيارية
  • الفرق عن Observer: المفوض هو علاقة 1:1، بينما تعمل الإشعارات على مبدأ 1:N

ما هو نمط Delegate؟

Delegate هو نمط تصميم سلوكي يسمح لكائن بتفويض جزء من مسؤولياته إلى كائن آخر. على عكس الوراثة، حيث تقوم الفئة الفرعية بتجاوز أساليب الفئة الأم، يستخدم المفوض التكوين: الكائن المالك يحتفظ بمرجع للمفوض ويستدعي أساليبه في نقاط محددة.

يحدد المفوض بروتوكولًا — مجموعة من الأساليب التي يمكن للمفوض تنفيذها. تنقسم الأساليب إلى إلزامية واختيارية. في Swift، يتم تمييز الأساليب الاختيارية للبروتوكول بالكلمة الأساسية @objc optional.

كيف يعمل المفوض

الكائن A (المالك) يحتوي على خاصية delegate — مرجع ضعيف للكائن B (المفوض). عند حدوث حدث، يتحقق A مما إذا كان B ينفذ طريقة البروتوكول المقابلة ويستدعيها. المرجع الضعيف إلزامي: بدونه، لا يمكن تحرير المفوض من الذاكرة لأن المالك يحتفظ به بمرجع قوي.

Swift
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 أن البروتوكول يمكن تنفيذه فقط بواسطة الفئات — وهذا ضروري للمرجع الضعيف.

كيف يعمل Delegate في Swift؟

تنفيذ المفوض في Swift يشمل ثلاث خطوات: إعلان البروتوكول، إنشاء خاصية delegate ضعيفة في المالك، وتنفيذ البروتوكول في فئة المفوض. لننظر إلى UITextField مخصص مع التحقق كمثال.

Swift
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(_:). يعمل المتحكم كوسيط بين View ومنطق التحقق.

Delegate مقابل NotificationCenter مقابل Closure

الاختيار بين المفوض والإشعارات و closures يعتمد على عدد المستقبلين واقتران المكونات. كل آلية تحل مشكلة التواصل بين الكائنات، ولكن بمقايضات مختلفة.

الخاصيةDelegateNotificationCenterClosure
نوع الاتصال1:11:N1:1
الاقترانضعيف (عبر بروتوكول)ضعيف جدًا (مفتاح نصي)متوسط (التقاط السياق)
أمان الأنواعكامللا يوجد (Any?)كامل
خطر دورة الاحتفاظلا (weak)لانعم (التقاط self)
متى تستخدماستدعاءات معقدة بأساليب متعددةأحداث تهم العديدمغلقات بسيطة بـ 1-2 استدعاء

Delegate مثالي عندما تحتاج إلى تمرير سلسلة من الأحداث ذات الصلة إلى مستلم واحد. NotificationCenter أفضل للإشعارات الواسعة. Closure للعمليات غير المتزامنة البسيطة، مثل معالجات الإكمال في URLSession.

تنفيذ Delegate في Objective-C

Objective-C يستخدم @protocol و @optional لإعلان المفوضين. على عكس Swift، جميع أساليب البروتوكول اختيارية افتراضيًا. الفرق الرئيسي هو استدعاء respondsToSelector: قبل إرسال رسالة إلى المفوض، لأن الطريقة قد لا تكون منفذة.

Objective-C
@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، الأساليب الاختيارية للبروتوكول تلغي هذا التحقق — السلسلة الاختيارية (?.) تتعامل تلقائيًا مع غياب التنفيذ.

الأخطاء الشائعة عند العمل مع المفوضين

أخطاء استخدام المفوضين تؤدي إلى تسريبات الذاكرة، وتعطل التطبيق، وأخطاء غير واضحة. لننظر إلى خمس مشاكل شائعة.

مرجع قوي للمفوض

دورة الاحتفاظ هي الخطأ الأكثر شيوعًا. إذا تم إعلان خاصية delegate كـ strong، وكان المفوض بدوره يمتلك الكائن المالك، تتشكل دورة احتفاظ. لن يتم تحرير كلا الكائنين من الذاكرة أبدًا. الحل: دائمًا أعلن المفوض كـ weak var في Swift أو @property (weak) في Objective-C.

المفوض لم يُلغَ بعد التحرير

إذا نجا الكائن المالك من المفوض وبقي المرجع، فإن استدعاء طريقة المفوض سيؤدي إلى EXC_BAD_ACCESS. المرجع الضعيف يحل هذه المشكلة تلقائيًا: بعد تحرير المفوض، تصبح الخاصية nil. ومع ذلك، في السيناريوهات متعددة الخيوط، يجب التحقق إضافيًا من المفوض في الخيط الرئيسي.

عدد مفرط من الأساليب في البروتوكول

بروتوكول بأكثر من 20 طريقة ينتهك مبدأ فصل الواجهات (ISP). UITableViewDelegate يحتوي على حوالي 30 طريقة اختيارية — وهذا استثناء تاريخي. في بروتوكولاتك الخاصة، من الأفضل تقسيم المسؤولية إلى عدة بروتوكولات أصغر، لكل منها دوره الخاص.

أمثلة Delegate في واجهات النظام iOS

واجهات Apple النظامية تستخدم نمط Delegate بنشاط. لننظر إلى ثلاثة أمثلة رئيسية من UIKit تظهر في كل تطبيق iOS.

APIالبروتوكولالأساليب الرئيسية
UITableViewUITableViewDelegatedidSelectRowAt, heightForRowAt, willDisplay
UITextFieldUITextFieldDelegateshouldChangeCharactersIn, didBeginEditing, shouldReturn
URLSessionURLSessionDelegatedidReceiveChallenge, didCompleteWithError, didBecomeInvalidWithError

كل من هذه البروتوكولات تنفذ جوانب مختلفة من السلوك: UITableViewDelegate يدير المظهر والاستجابة للمس، UITextFieldDelegate يتحكم في إدخال النص، URLSessionDelegate يعالج أحداث الشبكة. هذا يوضح مرونة النمط: يمكن تكييف المفوض لأي مجال من المسؤولية.

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

ما الفرق بين delegate و dataSource في iOS؟

Delegate يدير السلوك والمظهر (ارتفاع الخلية، الاستجابة للمس). DataSource يوفر البيانات (عدد الصفوف، محتويات الخلايا). في UITableViewDelegate و UITableViewDataSource — هذان بروتوكولان منفصلان يقسمان المسؤولية بين العرض والبيانات.

لماذا يتم إعلان delegate كـ weak؟

المرجع الضعيف يمنع دورات الاحتفاظ. المالك (مثل UITableView) يحتفظ بمرجع للمفوض فقط كـ weak. إذا كان المفوض (UIViewController) يمتلك الجدول، فإن المرجع القوي للمفوض سيخلق دورة: ViewController → UITableView → Delegate (ViewController). Weak يكسر هذه الدورة.

هل يمكن استخدام delegate في SwiftUI؟

في SwiftUI، يتم استخدام نمط Delegate بشكل أقل — يتم استبداله بـ @Binding و @State و closures. ومع ذلك، لا يزال المفوض يُستخدم لتكامل UIKit عبر UIViewRepresentable. على سبيل المثال، MKMapViewDelegate و WKUIDelegate يظلان ذو صلة عند تغليف مكونات UIKit في SwiftUI.

ما هو @objc optional في بروتوكولات Swift؟

@objc optional يسمح بإعلان أساليب اختيارية في بروتوكول Swift. هذه آلية توافق مع بيئة Objective-C. بدون @objc، جميع أساليب بروتوكول Swift إلزامية افتراضيًا. يُستخدم Optional في بروتوكولات UIKit حيث يمكن للمفوض تنفيذ فقط الأساليب التي يحتاجها.

كم عدد المفوضين الذي يمكن أن يمتلكه كائن واحد؟

يمكن أن يمتلك كائن واحد مفوضًا واحدًا فقط لكل خاصية delegate. إذا كنت بحاجة إلى إعلام كائنات متعددة، استخدم مفوضًا متعددًا (multicast delegate)، أو مصفوفة من المفوضين، أو NotificationCenter. نمط Delegate مصمم أصلاً كعلاقة 1:1.

الملخص

  • Delegate — نمط تصميم سلوكي ينقل تنفيذ مهمة من كائن إلى آخر عبر بروتوكول
  • بروتوكول Swift يعلن مجموعة من الأساليب؛ الأساليب الاختيارية تُوسم بـ @objc optional، الأساليب الإلزامية تُنفذ دائمًا
  • المرجع الضعيف للمفوض إلزامي لمنع دورات الاحتفاظ وتسريبات الذاكرة
  • UITableViewDelegate هو المثال الأكثر شهرة؛ أساليبه تدير ارتفاع الخلايا واختيارها وعرضها
  • Delegate يختلف عن NotificationCenter بعلاقة 1:1 مقابل 1:N وأمان كامل للأنواع
  • Objective-C يتطلب التحقق بـ respondsToSelector: للأساليب الاختيارية، Swift يتعامل مع هذا تلقائيًا
  • توصية: استخدم delegate للاستدعاءات المعقدة بأساليب متعددة ذات الصلة و closure للمغلقات البسيطة

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

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

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

اقرأ أيضًا