ما هو @selector() في Swift ولماذا هو مطلوب؟
إذا كنت قد عملت مع Objective-C من قبل أو صادفت كودًا قديمًا، فبالتأكيد سمعت عن البنية السحرية @selector(). في Swift، تحولت إلى #selector، لكن الجوهر بقي نفسه: إنه جسر بين الكتابة الثابتة لـ Swift والطبيعة الديناميكية لبيئة Objective-C runtime.
تخيل أنك بحاجة إلى تمرير مرجع إلى دالة كوسيط. على سبيل المثال، لمعالجة الضغط على زر أو الاشتراك في الإشعارات. في Swift، اعتدنا على استخدام الإغلاقات (closures)، لكن مكتبات Apple (UIKit، AppKit، Foundation) لا تزال تعتمد على آلية المحددات القديمة الجيدة. هنا يصبح @selector() في Swift أداة لا غنى عنها.
في هذا الدليل، سنشرح:
- كيفية تعريف المحددات بشكل صحيح في Swift الحديث
- الأخطاء الشائعة وكيفية تجنبها
- أمثلة حقيقية من تطوير iOS
- متى تستخدم
#selectorومتى تستخدم البدائل
من @selector() إلى #selector: تطور الصياغة
في Objective-C، يبدو المحدد كالتالي:
[@selector(buttonTapped:)]في Swift 2.0، ظهرت البنية #selector التي أصبحت المعيار. إليك كيف تبدو الآن:
#selector(MyClass.buttonTapped(_:))الفرق الرئيسي: #selector في Swift يتم التحقق منه بواسطة المترجم. إذا أخطأت في كتابة اسم الدالة أو مررت توقيعًا غير صحيح، سيعطيك المترجم خطأ. هذه ميزة كبيرة مقارنة بـ Objective-C، حيث المحدد هو مجرد سلسلة نصية، ويظهر الخطأ فقط في وقت التشغيل (runtime).
قواعد تعريف الدوال لـ #selector
يجب أن تكون الدالة التي تمررها إلى #selector:
- متاحة من Objective-C — مُعلَّمة بالسمة
@objc - غير خاصة (أو مُعلَّمة كـ
@objc private) - متوافقة في التوقيع مع ما تتوقعه الجهة المستدعية
مثال على تعريف صحيح:
class ViewController: UIViewController { @objc func buttonTapped(_ sender: UIButton) { print("تم الضغط على الزر!") } override func viewDidLoad() { super.viewDidLoad() let button = UIButton(type: .system) button.addTarget(self, action: #selector(buttonTapped(_:)), for: .touchUpInside) }}لاحظ @objc قبل الدالة. بدونه، لن يسمح لك المترجم باستخدام #selector.
@selector() Swift: أمثلة عملية
1. Target-Action: الأزرار والإيماءات
السيناريو الأكثر شيوعًا لاستخدام #selector هو ربط الإجراءات بعناصر واجهة المستخدم:
class ProfileViewController: UIViewController { @objc private func avatarTapped(_ gesture: UITapGestureRecognizer) { print("تم الضغط على الصورة الرمزية") } @objc private func settingsButtonPressed() { print("فتح الإعدادات") } override func viewDidLoad() { super.viewDidLoad() // الإيماءة let tapGesture = UITapGestureRecognizer(target: self, action: #selector(avatarTapped(_:))) avatarImageView.addGestureRecognizer(tapGesture) // الزر settingsButton.addTarget(self, action: #selector(settingsButtonPressed), for: .touchUpInside) }}تذكر: إذا كانت الدالة تقبل معاملًا (مثل UITapGestureRecognizer)، يجب عليك تحديد _ في توقيع المحدد: #selector(avatarTapped(_:)).
2. PerformSelector والاستدعاءات المؤجلة
أحيانًا تحتاج إلى استدعاء دالة ديناميكيًا، خاصة عند العمل مع KVO أو المؤقتات:
class TimerManager { @objc func fireTimer() {