Dispatch_once في Swift: لماذا هو خطأ وكيفية كتابة singleton صحيح
\n\nإذا تساءلت يومًا "كيفية إنشاء singleton في Swift باستخدام dispatch_once؟" — فأنت لست وحدك. على Stack Overflow، حظي هذا السؤال بآلاف المشاهدات، ولا تزال في كود العديد من المشاريع تركيبات غريبة تحاول محاكاة نهج Objective-C القديم. لكن الخبر السار هو أن هناك حلاً أكثر أناقة وأمانًا في Swift. الخبر السيئ: يستمر العديد من المطورين في ارتكاب نفس الأخطاء، محاولين نقل عاداتهم من Objective-C إلى Swift.
\n\nفي هذه المقالة، سنستعرض أهم 5 أخطاء عند استخدام dispatch_once في Swift، ونوضح كيفية تجنبها، ونقدم قالبًا جاهزًا لـ singleton صحيح. هيا بنا!
\n\nما هو dispatch_once ولماذا لا نحتاجه في Swift؟
\n\ndispatch_once هي دالة من Grand Central Dispatch (GCD) تضمن تنفيذ كتلة من الكود مرة واحدة فقط طوال عمر التطبيق. في Objective-C، كانت هذه هي الطريقة القياسية لإنشاء singletons آمنة للخيوط:
\n\n+ (instancetype)sharedInstance {\n static MyClass *sharedInstance = nil;\n static dispatch_once_t onceToken;\n dispatch_once(&onceToken, ^{\n sharedInstance = [[MyClass alloc] init];\n });\n return sharedInstance;\n}\n\nومع ذلك، في Swift، بدءًا من الإصدار 1.0، dispatch_once غير مدعوم للاستخدام في كود المستخدم. نعم، يمكنك استيراد Dispatch ومحاولة استدعائها، لكن المترجم لن يسمح بذلك. لماذا؟ لأن Swift يقدم أدوات مدمجة تجعل dispatch_once غير ضرورية.
أهم 5 أخطاء عند استخدام dispatch_once في Swift
\n\nالخطأ 1: محاولة استدعاء dispatch_once مباشرة
\n\nالخطأ الأكثر شيوعًا هو محاولة كتابة شيء مثل:
\n\nclass MySingleton {\n static var shared: MySingleton = {\n let instance = MySingleton()\n // إعدادات\n return instance\n }()\n \n private init() {}\n}\n\nللوهلة الأولى، هذا يعمل. لكن هذا ليس dispatch_once. إنه مجرد متغير عام يتم تهيئته بتكاسل (lazy) عند أول وصول. يضمن Swift سلامة الخيوط لهذه الخصائص الثابتة، لكن هذا ليس نفس dispatch_once.
\n\nلماذا هذا سيء؟ لأنك تفقد السيطرة على لحظة التهيئة. إذا كان الـ singleton الخاص بك يعتمد على موارد خارجية (مثل قاعدة بيانات أو شبكة)، فقد لا تكون جاهزة بعد.
\n\nالخطأ 2: استخدام dispatch_once عبر غلاف Objective-C
\n\nيقوم بعض المطورين بإنشاء جسر إلى Objective-C لاستدعاء dispatch_once من Swift:
\n\n// ملف Objective-C\n@interface OnceHelper : NSObject\n+ (void)performOnce:(void(^)(void))block;\n@end\n\n@implementation OnceHelper\nstatic dispatch_once_t onceToken;\n+ (void)performOnce:(void(^)(void))block {\n dispatch_once(&onceToken, block);\n}\n@end\n\nثم في Swift:
\n\nclass MySingleton {\n static var shared: MySingleton = {\n var instance: MySingleton!\n OnceHelper.performOnce {\n instance = MySingleton()\n }\n return instance\n }()\n}\n\nما المشكلة؟ أولاً، هذا حل مؤقت. ثانيًا، dispatch_once_t هو متغير عام، وإذا نسيت إعادة تعيين الرمز (على سبيل المثال، أثناء الاختبار)، فلن يتم إعادة إنشاء الـ singleton. ثالثًا، توصي Apple صراحةً بعدم استخدام dispatch_once في Swift.
\n\nالخطأ 3: نسيان سلامة الخيوط
\n\nيكتب البعض الـ singleton بهذه الطريقة:
\n\nclass MySingleton {\n static var shared: MySingleton?\n \n private init() {}\n \n static func getInstance() -> MySingleton {\n if shared == nil {\n shared = MySingleton()\n }\n return shared!\n }\n}<