dispatch_after في Swift: GCD تحت الغطاء — تحليل مع أمثلة

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

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

ابدأ الدورة

dispatch_after في Swift: كيف ينفذ GCD المهام المؤجلة تحت الغطاء

\n\n

تخيل: أنت تكتب تطبيقًا بلغة Swift، وتحتاج إلى تنفيذ جزء من الكود ليس فورًا، بل، لنقل، بعد 3 ثوانٍ. أول ما يتبادر إلى الذهن هو dispatch_after. ولكن إذا ألقيت نظرة على الإصدارات القديمة من Swift أو على Stack Overflow، فسترى أن هذه الطريقة قد تغيرت. والأهم من ذلك، أن القليل من يفهم ما يحدث بالفعل داخل Grand Central Dispatch (GCD) عندما نقول: "افعل هذا بعد ثانيتين".

\n\n

في هذه المقالة، لن نعرض فقط كيفية كتابة dispatch_after في Swift الحديث. بل سنتعمق تحت غطاء GCD، ونحلل الطوابير، والخيوط، والمؤقتات، لتتمكن من كتابة كود غير متزامن موثوق وفعال. هيا بنا!

\n\n

ما هو dispatch_after ولماذا أصبح asyncAfter؟

\n\n

في الإصدارات القديمة من Swift (1.x، 2.x)، قد تصادف هذا الكود:

\n\n
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(2 * NSEC_PER_SEC)), dispatch_get_main_queue()) {\n    print("مرت ثانيتان")\n}
\n\n

كان هذا الكود يعمل، لكنه يبدو ضخمًا. بدءًا من Swift 3، قامت Apple بإعادة هيكلة واجهة GCD API بأكملها، مما جعلها أكثر "سويفتية". الآن، نفس الوظيفة تبدو كالتالي:

\n\n
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {\n    print("مرت ثانيتان")\n}
\n\n

لاحظ: asyncAfter هي طريقة تابعة للطابور (DispatchQueue)، وليست دالة منفصلة. هذا تغيير جوهري: الآن أنت تحدد بوضوح أي طابور سينفذ المهمة.

\n\n

كيف يعمل GCD تحت الغطاء: الطوابير، الخيوط، والمؤقتات

\n\n

لفهم dispatch_after (أو asyncAfter)، تحتاج إلى فهم ثلاث كيانات:

\n\n
    \n
  • DispatchQueue — طابور المهام. يمكن أن يكون تسلسليًا (serial) أو متوازيًا (concurrent).
  • \n
  • DispatchWorkItem — المهمة التي تضعها في الطابور. في حالتنا، هذا هو الإغلاق (closure).
  • \n
  • DispatchTime — اللحظة الزمنية التي يجب أن تبدأ فيها المهمة. يستخدم GCD مؤقتًا نظاميًا لتتبع المواعيد النهائية.
  • \n
\n\n

عندما تستدعي asyncAfter، لا يقوم GCD بإنشاء خيط جديد. بدلاً من ذلك، يقوم بـ:

\n\n
    \n
  1. إنشاء مؤقت (timer) سيعمل في الوقت المحدد.
  2. \n
  3. وضع المهمة في طابور خاص بالمهام المؤجلة (يُسمى غالبًا dispatch timer).
  4. \n
  5. عندما يعمل المؤقت، ينقل GCD المهمة إلى الطابور العادي DispatchQueue، حيث تنتظر خيطها.
  6. \n
\n\n

إذا كان الطابور الذي تضع فيه المهمة مشغولاً بمهام أخرى، فقد يتم تنفيذ مهمتك المؤجلة بعد الموعد النهائي المحدد. هذه نقطة مهمة: dispatch_after يضمن أن المهمة لن تُنفذ قبل الموعد النهائي، ولكن قد تُنفذ بعده.

\n\n

مثال عملي: dispatch_after لتحديث واجهة المستخدم

\n\n

السيناريو الأكثر شيوعًا هو تحديث الواجهة بعد فترة زمنية من إجراء المستخدم. لنفترض أنك تعرض رسالة "تم الحفظ"، وبعد ثانيتين تقوم بإخفائها:

\n\n
func showTemporaryMessage() {\n    let messageLabel = UILabel()\n    messageLabel.text = "تم الحفظ!"\n    messageLabel.alpha = 1.0\n    view.addSubview(messageLabel)\n\n    DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {\n        UIView.animate(withDuration: 0.5) {\n            messageLabel.alpha = 0.0\n        } completion: { _ in\n            messageLabel.removeFromSuperview()\n        }\n    }\n}
\n\n

لاحظ: نحن نستخدم DispatchQueue.main لأن جميع العمليات المتعلقة بواجهة المستخدم يجب أن تُنفذ على الخيط الرئيسي. إذا استدعينا asyncAfter على طابور خلفي ثم حاولنا تغيير واجهة المستخدم، فسينهار التطبيق.

\n\n

المطبات الخفية في dispatch_after: تسرب الذاكرة والاستيلاء على self

\n\n

أحد الأخطاء الأكثر شيوعًا هو المرجع القوي لـ self داخل

المدونات

توصيات الكتب