Dispatch_once في Swift: الأخطاء الرئيسية والبدائل الصحيحة

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

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\n

dispatch_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 غير ضرورية.

\n\n

أهم 5 أخطاء عند استخدام dispatch_once في Swift

\n\n

الخطأ 1: محاولة استدعاء dispatch_once مباشرة

\n\n

الخطأ الأكثر شيوعًا هو محاولة كتابة شيء مثل:

\n\n
class 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\n
class 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\n
class 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}<                

المدونات

توصيات الكتب