كيف يميز Swift بين المحاكي والجهاز الحقيقي: تحليل كامل

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

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

ابدأ الدورة

كيف يميز Swift بين المحاكي والجهاز الحقيقي؟ تحليل كامل تحت الغطاء

\n\n

كل مطور iOS واجه مرة واحدة على الأقل موقفًا يعمل فيه الكود بشكل مثالي في المحاكي، لكنه يتعطل على الجهاز الحقيقي. أو العكس - وظيفة تستخدم الكاميرا أو مقياس التسارع غير متاحة في المحاكي. في مثل هذه اللحظات، من الضروري للغاية معرفة التمييز بين مكان تشغيل التطبيق: على iPhone/iiPad فعلي أو في بيئة Xcode الافتراضية. هذا الموضوع هو ضجة حقيقية على Stack Overflow (آلاف المشاهدات!)، واليوم سنقوم بتحليله حتى النهاية. ستتعلم ليس فقط الحلول الجاهزة، بل ستفهم أيضًا كيف تعمل على مستوى المترجم والمعالج المسبق.

\n\n

لماذا هذا ضروري أصلاً؟

\n

المحاكي أداة قوية، لكنه لا يحاكي كل الأجهزة. على سبيل المثال، لن تتمكن من الوصول إلى:

\n
    \n
  • الكاميرا (محاكاة فقط عبر كاميرا Mac)
  • \n
  • مقياس التسارع والجيروسكوب
  • \n
  • وحدة الاتصال الحقيقية
  • \n
  • Face ID (فقط في المحاكي مع T2/M1)
  • \n
  • شريحة Secure Enclave
  • \n
\n

بالإضافة إلى ذلك، بنى المعالجات مختلفة: على الجهاز الحقيقي - ARM64، وعلى المحاكي (على Mac مع Intel) - x86_64. حتى على Apple Silicon، يعمل المحاكي عبر Rosetta 2، مما يضيف فروقًا دقيقة خاصة به. لهذه الحالات تحديدًا نحتاج إلى التحقق من المحاكي في Swift.

\n\n

الطريقة التقليدية: TARGET_OS_SIMULATOR

\n

الطريقة الأكثر شيوعًا والتي توصي بها Apple هي استخدام توجيه المعالج المسبق TARGET_OS_SIMULATOR. وهو معرف في ملف الرأس TargetConditionals.h، الذي يتم تضمينه تلقائيًا في كل مشروع Swift عبر وحدة Darwin.

\n\n

إليك كيف يبدو الكود القياسي:

\n\n
#if targetEnvironment(simulator)\n    print("تم التشغيل في المحاكي")\n    // هنا يمكن محاكاة سلوك الكاميرا أو GPS\n#else\n    print("تم التشغيل على جهاز حقيقي")\n    // كاميرا حقيقية، GPS حقيقي\n#endif
\n\n

بسيط، أنيق، ويعمل. لكن دعنا نتعمق أكثر - ماذا يحدث في الواقع عندما يرى المترجم هذا البناء؟

\n\n

تحت الغطاء: كيف يعمل targetEnvironment

\n

في Swift، بناء #if targetEnvironment(simulator) ليس سحرًا، بل هو نتيجة عمل المعالج المسبق. عندما يقوم Xcode ببناء المشروع للمحاكي، يضيف علم مترجم خاص -DTARGET_OS_SIMULATOR=1. وبالمقابل، للجهاز الحقيقي، هذا العلم يساوي 0 أو غير موجود.

\n\n

إليك سلسلة التحويلات:

\n
    \n
  1. يحدد Xcode إلى أين سيتم البناء (مخطط البناء)
  2. \n
  3. إذا تم اختيار المحاكي - يتم إضافة -D TARGET_OS_SIMULATOR إلى OTHER_SWIFT_FLAGS
  4. \n
  5. يرى مترجم Swift هذا الثابت ويعالج كتلة #if
  6. \n
  7. يتم قطع كل الكود داخل كتلة #else (للجهاز الحقيقي) من الملف الثنائي
  8. \n
\n\n

هذا يعني أن هذا الكود لا يتم تنفيذه فقط أثناء وقت التشغيل - بل يتم تجميعه بشكل مختلف لمنصات مختلفة. تحصل على ملفين ثنائيين مختلفين، ولا يحتوي كل منهما على كود زائد. توفير في المساحة والسرعة!

\n\n

طريقة بديلة: التحقق من البنية

\n

قبل ظهور targetEnvironment، كان المطورون يستخدمون حيلة ذكية بأعلام البنية. لا تزال تعمل حتى اليوم وتكون مفيدة أحيانًا للتوافق العكسي.

\n\n
#if arch(x86_64) || arch(i386)\n    print("محاكي (Intel Mac)")\n#elseif arch(arm64)\n    print("جهاز حقيقي أو محاكي على Apple Silicon")\n#endif
\n\n

لكن هناك خدعة هنا! على Mac مع Apple Silicon (M1، M2، M3)، يعمل المحاكي أيضًا على ARM64. لذلك، مجرد التحقق من arch(arm64) غير كافٍ - سيكون صحيحًا لكل من iPhone الحقيقي والمحاكي على M1. لهذا السبب تحديدًا قدمت Apple targetEnvironment(simulator) - فهو مصمم خصيصًا لهذه المهمة.

\n\n

دقائق العمل على Apple Silicon

\n

منذ عام 2020، عندما صدرت أجهزة Mac على ARM، مع

المدونات

توصيات الكتب