أعتقد أنه كان على الكثيرين في عملية تطوير لعبة لنظام iOS مواجهة حقيقة أنه أصبح من الضروري استخدام وظيفة أصلية أو أخرى. فيما يتعلق بـ Unity3D ، يمكن أن تنشأ الكثير من المشاكل في هذه المشكلة: من أجل تنفيذ نوع من الميزات ، عليك أن تنظر إلى المكونات الإضافية الأصلية المكتوبة في Objective-C. شخص ما في هذه اللحظة يأس على الفور ويتخلى عن الفكرة. يبحث شخص ما عن حلول جاهزة في AssetStore أو في المنتديات ، على أمل وجود حل جاهز بالفعل. إذا لم تكن هناك حلول جاهزة ، فإن الأكثر إلحاحًا منا لا يرون أي طريقة أخرى سوى الانغماس في هاوية برمجة iOS وتفاعل Unity3D مع رمز Objective-C.
أولئك الذين يختارون المسار الأخير (على الرغم من أنني أعتقد أنهم يعرفون أنفسهم) ، سيواجهون العديد من المشاكل على هذا الطريق الصعب والشائك:
- iOS هو نظام بيئي معزول وغير مألوف تمامًا ، يتطور بطريقته الخاصة. كحد أدنى ، سيكون عليك قضاء الكثير من الوقت لفهم كيفية الوصول إلى التطبيق ، وأين يكون في أحشاء مشروع Xcode الذي تم إنشاؤه تلقائيًا هو رمز محرك Unity3D للتفاعل مع المكون الأصلي للتطبيق.
- الهدف جيم هو لغة برمجة منفصلة إلى حد ما وشبه قليلا. وعندما يتعلق الأمر بالتفاعل مع كود C ++ الخاص بتطبيق Unity3D ، فإن "لهجة" هذه اللغة ، المسماة Objective-C ++ ، تدخل إلى المشهد. هناك القليل من المعلومات عنه ، معظمها قديمة وأرشيفية.
- بروتوكول التفاعل بين Unity3D وتطبيق iOS موصوف بشكل سيئ. يجب أن تعتمد فقط على البرامج التعليمية لهواة الشبكة الذين يكتبون كيفية تطوير أبسط مكون إضافي أصلي. في نفس الوقت ، قلة من الناس يتطرقون إلى قضايا ومشكلات أعمق ناشئة عن الحاجة إلى القيام بشيء معقد.
أولئك الذين يرغبون في التعرف على آليات تفاعل Unity3D مع تطبيق iOS ، من فضلك ، تحت القط.
من أجل توضيح الاختناق الشديد للتفاعل بين Unity3D مع الشفرة الأصلية ، تصف هذه المقالة جوانب التفاعل لمفوض تطبيق iOS برمز Unity3D ، التي يتم بها استخدام أدوات C ++ و Objective-C ، وكيفية تعديل تفويض التطبيق بنفسك. يمكن أن تكون هذه المعلومات مفيدة لفهم أفضل لآليات الربط Unity3D + iOS ، والاستخدام العملي.
التفاعل بين iOS والتطبيق
كمقدمة ، دعونا نلقي نظرة على كيفية تنفيذ تفاعل التطبيق مع النظام في iOS والعكس بالعكس. من الناحية التخطيطية ، يبدو إطلاق تطبيق iOS كما يلي:

لدراسة هذه الآلية من وجهة نظر التعليمات البرمجية ، يعد تطبيقًا جديدًا تم إنشاؤه في Xcode باستخدام قالب "تطبيق عرض واحد" مناسبًا.

باختيار هذا القالب ، سيعطيك الناتج أبسط تطبيق iOS يمكن تشغيله على جهاز أو محاكي ويظهر شاشة بيضاء. من المفيد أن تنشئ Xcode مشروعًا لن يكون فيه سوى 5 ملفات برمز مصدر (2 منها ملفات رأس .h) والعديد من الملفات الإضافية التي لا تهمنا (التنضيد ، والتكوينات ، والرموز).

دعونا نرى ما هي ملفات التعليمات البرمجية المصدر المسؤولة عن:
- ViewController.m / ViewController.h - أكواد مصدر ليست مثيرة للاهتمام بالنسبة لنا. نظرًا لأن تطبيقك يحتوي على طريقة عرض (لا يتم تمثيلها برمز ، ولكن باستخدام لوحة العمل) ، ستحتاج إلى فئة وحدة التحكم ، والتي ستتحكم في طريقة العرض هذه. بشكل عام ، بهذه الطريقة تشجعنا Xcode نفسها على استخدام نمط MVC. لن يحتوي المشروع الذي يقوم بإنشاء Unity3D على ملفات المصدر هذه.
- AppDelegate.m / AppDelegate.h هو مفوض التطبيق الخاص بك. نقطة الاهتمام في التطبيق حيث يبدأ عمل رمز التطبيق المخصص.
- main.m - نقطة البداية للتطبيق. بطريقة أي تطبيق C / C ++ ، فإنه يحتوي على الوظيفة الرئيسية ، التي يبدأ بها البرنامج.
الآن ، دعنا نرى الرمز الذي يبدأ بملف
main.m :
int main(int argc, char * argv[]) {
مع السطر 1 ، كل شيء واضح وبدون تفسير ، دعنا ننتقل إلى السطر 2. وهو يشير إلى أن دورة حياة التطبيق ستحدث داخل تجمع Autorelease. يخبرنا استخدام التجمع التلقائي ، أننا سوف نعهد بإدارة ذاكرة التطبيق إلى هذا التجمع المحدد ، أي أنه سيتعامل مع المشكلات عندما يكون من الضروري تحرير الذاكرة لمتغير معين. إن قصة إدارة الذاكرة على iOS خارج نطاق هذه القصة ، لذلك ليس هناك جدوى من الخوض في هذا الموضوع. بالنسبة لأولئك المهتمين بهذا الموضوع ، يمكنك أن تجد ، على سبيل المثال ،
هذه المقالة .
دعنا
ننتقل إلى السطر 3. إنه يستدعي وظيفة
UIApplicationMain . يتم تمرير معلمات بدء تشغيل البرنامج (argc ، argv) إليها. ثم ، في هذه الوظيفة ، يشار إلى الفئة التي يجب استخدامها كفئة رئيسية للتطبيق ، يتم إنشاء مثيله. وأخيرًا ، تتم الإشارة إلى الفئة التي سيتم استخدامها كمفوض للتطبيق ، ويتم إنشاء مثيله ، ويتم تكوين الاتصالات بين مثيل فئة التطبيق والمفوض الخاص به.
في مثالنا ، يتم تمرير لا شيء على أنه الفصل الذي سيمثل مثيل التطبيق - تقريبًا ، النظير المحلي فارغ. بالإضافة إلى لا شيء ، يمكنك تمرير فئة معينة موروثة من تطبيق
UIA هناك . إذا تم تحديد لا شيء ، فسيتم استخدام UIApplication. هذه الفئة هي نقطة مركزية لإدارة وتنسيق عمل أي تطبيق على نظام iOS وهي لغة فردية. باستخدامه ، يمكنك معرفة كل شيء تقريبًا حول الحالة الحالية للتطبيق والإشعارات والنوافذ والأحداث التي حدثت في النظام نفسه والتي تؤثر على هذا التطبيق وأكثر من ذلك بكثير. هذه الفئة لا ترث أبدًا. سوف نتحدث عن إنشاء فئة مندوب التطبيق بمزيد من التفصيل.
إنشاء مفوض التطبيق
إشارة إلى الفئة التي سيتم استخدامها كمفوض للتطبيق في استدعاء دالة
NSStringFromClass([AppDelegate class])
دعونا نحلل هذه المكالمة في أجزاء.
[AppDelegate class]
تقوم هذه البنية بارجاع عنصر من فئة AppDelegate (والذي تم تعريفه في AppDelegate.h / .m) ، وتقوم دالة
NSStringFromClass بارجاع اسم الفئة كسلسلة. نقوم ببساطة بتمرير اسم السلسلة للفئة التي سيتم إنشاؤها واستخدامها كمفوض للدالة UIApplicationMain. لفهم أفضل ، يمكن استبدال السطر 3 في ملف
main.m بما يلي:
return UIApplicationMain(argc, argv, nil, @"AppDelegate");
وستكون نتيجة تنفيذه مطابقة للنسخة الأصلية. على ما يبدو ، قرر المطورون اتباع هذا النهج من أجل عدم استخدام ثابت السلسلة. مع اتباع نهج قياسي ، إذا قمت بإعادة تسمية فئة المفوض ، فسيقوم المحلل اللغوي بإلقاء خطأ على الفور. في حالة استخدام السطر المعتاد ، سيتم تجميع الرمز بنجاح ، وستتلقى خطأ فقط عن طريق بدء التطبيق.
قد تشبهك آلية مماثلة لإنشاء فئة باستخدام اسم السلسلة فقط ، بالانعكاس من C #. الهدف- C ووقت تشغيله أقوى بكثير من الانعكاس في C #. هذه نقطة مهمة تمامًا في سياق هذه المقالة ، ولكن الأمر سيستغرق الكثير من الوقت لوصف جميع الميزات. ومع ذلك ، سنظل نلتقي بـ "التأمل" في الهدف- C أدناه. يبقى أن نفهم مفهوم مندوب التطبيق ووظائفه.
مندوب التطبيق
يحدث كل تفاعل للتطبيق مع iOS في فئة تطبيق UIA. تتحمل هذه الفئة الكثير من المسؤوليات - تنبه عن أصل الأحداث ، وحالة التطبيق وأكثر من ذلك بكثير. بالنسبة للجزء الأكبر ، دوره هو الإخطار. ولكن عندما يحدث شيء ما في النظام ، يجب أن نكون قادرين على الاستجابة لهذا التغيير بطريقة أو بأخرى ، لأداء نوع من الوظائف المخصصة. إذا قام مثيل لفئة UIApplication بذلك ، فستبدأ هذه الممارسة في تشابه نهج يسمى
الكائن الإلهي . لذلك ، يجدر التفكير في تحرير هذه الفئة من جزء من مسؤولياتها.
ولهذه الأغراض يستخدم النظام البيئي iOS شيء مثل مفوض التطبيق. من الاسم نفسه ، يمكننا أن نستنتج أننا نتعامل مع نمط تصميم مثل
التفويض . باختصار ، نحن ننقل ببساطة مسؤولية معالجة الاستجابة لأحداث معينة من التطبيق إلى مفوض التطبيق. لهذا الغرض ، في مثالنا ، تم إنشاء فئة AppDelegate حيث يمكننا كتابة وظائف مخصصة ، مع ترك فئة UIApplication للعمل في وضع الصندوق الأسود. قد يبدو هذا النهج مثيرًا للجدل بالنسبة لشخص ما من حيث جمال تصميم الهندسة المعمارية ، لكن مؤلفي iOS يدفعوننا إلى هذا النهج وتستخدمه الغالبية العظمى من المطورين (إن لم يكن جميعهم).
للتحقق بصريًا من عدد المرات التي يتلقى فيها مفوض التطبيق رسالة معينة أثناء عمل التطبيق ، ألق نظرة على الرسم التخطيطي:

تشير المستطيلات الصفراء إلى استدعاءات طريقة أو أكثر من المفوضين استجابة لأحداث معينة من حياة التطبيق (دورة حياة التطبيق). يوضح هذا الرسم البياني الأحداث المتعلقة بالتغييرات في حالة التطبيق فقط ولا يعرض العديد من الجوانب الأخرى لمسؤولية المندوب ، مثل قبول الإشعارات أو التفاعل مع الأطر.
فيما يلي بعض الأمثلة حيث قد نحتاج إلى الوصول إلى مفوض تطبيق من Unity3D:
- التعامل مع الدفع والإخطارات المحلية
- تسجيل أحداث إطلاق التطبيق إلى التحليلات
- تحديد كيفية تشغيل التطبيق - الخلفية "النظيفة" أو الخروج
- كيف تم إطلاق التطبيق - عن طريق tach للإخطار ، باستخدام الإجراءات السريعة للشاشة الرئيسية أو فقط عن طريق tach on incon
- التفاعل مع WatchKit أو HealthKit
- فتح ومعالجة عناوين URL من تطبيق آخر. إذا كان عنوان URL هذا ينطبق على تطبيقك ، يمكنك معالجته في تطبيقك بدلاً من السماح للنظام بفتح عنوان URL هذا في المستعرض
هذه ليست القائمة الكاملة للسيناريوهات. بالإضافة إلى ذلك ، تجدر الإشارة إلى أن المندوب يعدل العديد من التحليلات وأنظمة الإعلان في الإضافات الأصلية الخاصة بهم.
كيف تنفذ Unity3D مفوض التطبيق
دعونا الآن نلقي نظرة على مشروع Xcode الذي تم إنشاؤه بواسطة Unity3D ومعرفة كيفية تنفيذ مفوض التطبيق في Unity3D. عند إنشاء نظام أساسي لنظام iOS ، يقوم Unity3D تلقائيًا بإنشاء مشروع Xcode لك ، والذي يستخدم الكثير من التعليمات البرمجية المعيارية. يتضمن رمز القالب هذا أيضًا رمز مفوض التطبيق. داخل أي مشروع تم إنشاؤه ، يمكنك العثور على الملفات
UnityAppController.h و
UnityAppController.mm . تحتوي هذه الملفات على رمز فئة UnityAppController التي تهمنا.
في الواقع ، يستخدم Unity3D نسخة معدلة من قالب "تطبيق عرض واحد". فقط في هذا القالب ، يستخدم Unity3D مفوض التطبيق ليس فقط للتعامل مع أحداث iOS ، ولكن أيضًا لتهيئة المحرك نفسه ، وإعداد المكونات الرسومية ، وأكثر من ذلك بكثير. هذا سهل الفهم إذا نظرت إلى الطريقة.
- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions
في كود فئة UnityAppController. يتم استدعاء هذه الطريقة في وقت تهيئة التطبيق ، عندما يمكنك نقل التحكم إلى رمزك المخصص. داخل هذه الطريقة ، على سبيل المثال ، يمكنك العثور على الأسطر التالية:
UnityInitApplicationNoGraphics([[[NSBundle mainBundle] bundlePath] UTF8String]); [self selectRenderingAPI]; [UnityRenderingView InitializeForAPI: self.renderingAPI]; _window = [[UIWindow alloc] initWithFrame: [UIScreen mainScreen].bounds]; _unityView = [self createUnityView]; [DisplayManager Initialize]; _mainDisplay = [DisplayManager Instance].mainDisplay; [_mainDisplay createWithWindow: _window andView: _unityView]; [self createUI]; [self preStartUnity];
حتى دون الخوض في تفاصيل ما تصنعه هذه التحديات ، يمكنك تخمين أنها مرتبطة بإعداد Unity3D للعمل. اتضح السيناريو التالي:
- يتم استدعاء الوظيفة الرئيسية من main.mm
- يتم إنشاء فئات مثيل التطبيق والمفوض الخاص به.
- يقوم مندوب التطبيق بإعداد محرك Unity3D وتشغيله
- يبدأ رمزك المخصص في العمل. إذا كنت تستخدم il2cpp ، فسيتم ترجمة شفرتك من C # إلى IL ثم إلى رمز C ++ ، الذي يدخل مباشرة في مشروع Xcode.
يبدو هذا النص البرمجي بسيطًا جدًا ومنطقيًا ، ولكنه يجلب معه مشكلة محتملة: كيف يمكننا تعديل مفوض التطبيق إذا لم يكن لدينا حق الوصول إلى شفرة المصدر عند العمل في Unity3D؟
Unity3D المتأثرة لتعديل مفوض التطبيق
يمكننا إلقاء نظرة على ملفات
AppDelegateListener.mm/.h . تحتوي على وحدات ماكرو تسمح لك بتسجيل أي فئة كمستمع للأحداث لمفوض التطبيق. هذا نهج جيد ، لسنا بحاجة إلى تعديل الرمز الحالي ، ولكن فقط أضف رمزًا جديدًا. ولكن له عيبًا كبيرًا: لا يتم دعم جميع أحداث التطبيق ولا توجد طريقة للحصول على معلومات حول تشغيل التطبيق.
ومع ذلك ، فإن الطريقة الأكثر وضوحًا للخروج غير المقبولة هي تغيير شفرة مصدر التفويض يدويًا بعد إنشاء Unity3D لمشروع Xcode. المشكلة في هذا النهج واضحة - الخيار مناسب إذا قمت بعمل تجميعات بيديك ولم تشعر بالحيرة من الحاجة إلى تعديل الكود يدويًا بعد كل تجميع. في حالة استخدام المنشئين (Unity Cloud Build أو أي جهاز بناء آخر) ، فإن هذا الخيار غير مقبول على الإطلاق. لهذه الأغراض ، ترك لنا مطورو Unity3D ثغرة.
يحتوي ملف
UnityAppController.h ، بالإضافة إلى تعريف المتغيرات والأساليب ، أيضًا على تعريف ماكرو:
#define IMPL_APP_CONTROLLER_SUBCLASS(ClassName) ...
هذا الماكرو يجعل من الممكن تجاوز مفوض التطبيق. للقيام بذلك ، تحتاج إلى اتخاذ بعض الخطوات البسيطة:
- اكتب مندوب التطبيق الخاص بك في Objective-C
- في مكان ما داخل شفرة المصدر ، أضف السطر التالي
IMPL_APP_CONTROLLER_SUBCLASS(___)
- ضع هذا المصدر داخل مجلد Plugins / iOS في مشروع Unity3D الخاص بك
الآن ستتلقى مشروعًا يتم فيه استبدال مفوض تطبيق Unity3D القياسي بأحد مخصصك.
كيف يعمل الماكرو استبدال المفوض
دعونا نلقي نظرة على كود المصدر الكامل للماكرو:
#define IMPL_APP_CONTROLLER_SUBCLASS(ClassName) ... @interface ClassName(OverrideAppDelegate) \ { \ } \ +(void)load; \ @end \ @implementation ClassName(OverrideAppDelegate) \ +(void)load \ { \ extern const char* AppControllerClassName; \ AppControllerClassName = #ClassName; \ } \ @end
سيؤدي استخدام هذا الماكرو في المصدر إلى إضافة الرمز الموضح في الماكرو إلى نص المصدر في مرحلة التجميع. يقوم هذا الماكرو بما يلي. أولاً ، سيضيف طريقة التحميل إلى واجهة فصلك. يمكن اعتبار الواجهة في سياق الهدف جيم بمثابة مجموعة من المجالات والأساليب العامة. في C # ، ستظهر طريقة تحميل ثابتة في فصلك لا تُرجع شيئًا. بعد ذلك ، سيتم إضافة تطبيق طريقة التحميل هذه إلى رمز فصلك. في هذه الطريقة ، سيتم الإعلان عن المتغير AppControllerClassName ، وهو عبارة عن صفيف من النوع char ثم سيتم تعيين قيمة لهذا المتغير. هذه القيمة هي اسم السلسلة لفصلك. من الواضح أن هذه المعلومات ليست كافية لفهم آلية تشغيل هذا الماكرو ؛ لذلك ، يجب أن نفهم ما هي طريقة "الحمل" هذه ولماذا يتم الإعلان عن المتغير.
تقول
الوثائق الرسمية أن الحمل هو طريقة خاصة يتم استدعاؤها مرة واحدة لكل فئة (على وجه التحديد الفئة ، وليس مثيلاتها) في المرحلة المبكرة جدًا من إطلاق التطبيق ، حتى قبل استدعاء الوظيفة الرئيسية. ستقوم بيئة وقت تشغيل Objective-c (وقت التشغيل) عند بدء تشغيل التطبيق بتسجيل جميع الفئات التي سيتم استخدامها أثناء تشغيل التطبيق وستستدعي طريقة التحميل عليها ، إذا تم تنفيذها. اتضح أنه حتى قبل بدء أي رمز في تطبيقنا ، ستتم إضافة المتغير AppControllerClassName إلى فصلك.
ثم قد تعتقد: "وما الفائدة من وجود هذا المتغير إذا تم الإعلان عنه داخل الطريقة وسيتم حذفه من الذاكرة عند الخروج من هذه الطريقة؟". الإجابة على هذا السؤال تقع خارج حدود الهدف جيم.
وأين C ++؟
دعونا نلقي نظرة أخرى على إعلان هذا المتغير
extern const char* AppControllerClassName;
الشيء الوحيد الذي قد يكون غير مفهوم في هذا الإعلان هو المعدل الخارجي. إذا حاولت استخدام هذا المعدل في Objective-C النقي ، فسيؤدي Xcode إلى ظهور خطأ. والحقيقة أن هذا المعدل ليس جزءًا من Objective-C ؛ يتم تنفيذه في C ++. يمكن وصف الهدف- C بإيجاز شديد من خلال القول أنها "لغة سي مع الفصول". وهو امتداد للغة C ويسمح باستخدام غير محدود لرمز C يتخللها رمز Objective-C.
ومع ذلك ، لاستخدام الميزات الخارجية وميزات C ++ الأخرى ، تحتاج إلى القيام ببعض الحيل - استخدم Objective-C ++. لا توجد معلومات عمليًا عن هذه اللغة ، نظرًا لأنها رمز Objective-C فقط الذي يسمح بإدخال رمز C ++. من أجل أن يأخذ المترجم بعين الاعتبار أنه يجب ترجمة بعض الملفات المصدر إلى Objective-C ++ وليس Objective-C ، تحتاج فقط إلى تغيير امتداد هذا الملف من
.m إلى
.mm .
يتم استخدام المعدِّل الخارجي نفسه للإعلان عن متغير عام. بتعبير أدق ، لإخبار المترجم "صدقوني ، يوجد مثل هذا المتغير ، ولكن تم تخصيص ذاكرة له ليس هنا ، ولكن في مصدر آخر. ولديها أيضًا قيمة ، أنا أضمن ". وبالتالي ، فإن سطر الكود الخاص بنا يقوم ببساطة بإنشاء متغير عالمي ويخزن اسم فئتنا المخصصة فيه. يبقى فقط لفهم أين يمكن استخدام هذا المتغير.
عودة الى الصفحة الرئيسية
نتذكر ما قيل سابقًا - يتم إنشاء مفوض التطبيق عن طريق تحديد اسم الفئة. إذا تم إنشاء مفوض في نموذج Xcode للمشروع العادي باستخدام القيمة الثابتة [فئة myClass] ، فعندئذ ، على ما يبدو ، قرر الرجال من Unity أن هذه القيمة يجب أن تكون ملفوفة في متغير. باستخدام طريقة الوخزة العلمية ، نأخذ مشروع Xcode الذي تم إنشاؤه بواسطة Unity3D وننتقل إلى ملف
main.mm.نرى فيه رمزًا أكثر تعقيدًا من ذي قبل ، جزء من هذا الرمز مفقود باعتباره غير ضروري:
هنا نرى إعلان هذا المتغير للغاية ، وإنشاء مندوب التطبيق بمساعدته.
إذا أنشأنا مفوضًا مخصصًا ، فعندئذٍ يكون المتغير الضروري مهمًا بالفعل - اسم فصلنا. يضمن الإعلان عن المتغير وتهيئته قبل الوظيفة الرئيسية أن له قيمة افتراضية - UnityAppController.
الآن بهذا القرار يجب أن يكون كل شيء واضحًا جدًا.
مشكلة الماكرو
بالطبع ، بالنسبة للغالبية العظمى من الحالات ، يعد استخدام هذا الماكرو حلاً رائعًا. ولكن من الجدير بالذكر أن هناك مشكلة كبيرة: لا يمكن أن يكون لديك أكثر من مندوب مخصص. يحدث هذا لأنه إذا استخدمت فئتان أو أكثر الماكرو IMPL_APP_CONTROLLER_SUBCLASS (ClassName) ، فسيتم تعيين قيمة المتغير الذي نحتاجه لأول مرة ، وسيتم تجاهل التعيينات الأخرى. وهذا المتغير عبارة عن سلسلة ، أي أنه لا يمكن تعيين أكثر من قيمة واحدة.
قد تعتقد أن هذه المشكلة متدهورة وغير محتملة في الممارسة العملية. ولكن ، لم يكن هذا المقال سيحدث لو لم تحدث مثل هذه المشكلة بالفعل ، وحتى في ظل ظروف غريبة للغاية. قد يكون الوضع على النحو التالي. لديك مشروع تستخدم فيه العديد من التحليلات والخدمات الإعلانية. تحتوي العديد من هذه الخدمات على مكونات Objective-C. لقد كانوا في مشروعك لفترة طويلة وأنت لا تعرف المشاكل معهم. هنا تحتاج إلى كتابة مفوض مخصص. تستخدم الماكرو السحري المصمم لإنقاذك من المشاكل ، وبناء مشروع والحصول على تقرير عن نجاح التجميع. قم بتشغيل المشروع على الجهاز ، ولا تعمل وظائفك ولا تتلقى خطأ واحد.
وقد يكون الشيء هو أن أحد المكونات الإضافية للإعلان أو التحليلات يستخدم نفس الماكرو. على سبيل المثال ، في المكون الإضافي من
AppsFlyer يتم استخدام هذا الماكرو.
ما هي قيمة المتغير الخارجي في حالة الإعلانات المتعددة؟
من المثير للاهتمام أن نفهم ما إذا تم الإعلان عن المتغير الخارجي نفسه في عدة ملفات وتمت تهيئته بطريقة الماكرو (في طريقة التحميل) ، فكيف يمكننا فهم القيمة التي سيأخذها المتغير؟ لفهم النمط ، تم إنشاء تطبيق اختبار بسيط ، يمكن العثور على
رمزه هنا .
جوهر التطبيق بسيط. هناك فئتان A و B ، في كلا الفئتين يتم الإعلان عن المتغير الخارجي AexternVar ، ويتم تعيين قيمة معينة له. يتم تعيين قيم المتغير في الفئات بشكل مختلف. في الوظيفة الرئيسية ، يتم تسجيل قيمة هذا المتغير. وجد تجريبيا أن قيمة المتغير تعتمد على ترتيب إضافة المصادر إلى المشروع. يعتمد الترتيب الذي يسجل به وقت تشغيل Objective-C على الفئات أثناء تنفيذ التطبيق على ذلك. إذا كنت ترغب في تكرار التجربة ، فافتح المشروع وحدد علامة تبويب مراحل البناء في إعدادات المشروع. نظرًا لأن المشروع تجريبي وصغير ، فإنه لا يحتوي إلا على 8 رموز مصدر. كلهم موجودون في علامة تبويب مراحل البناء في قائمة مصادر الترجمة.

إذا كان مصدر الفئة A في هذه القائمة أعلى من مصدر الفئة B ، فسيأخذ المتغير قيمة من الفئة B. وإلا ، فسيأخذ المتغير قيمة من الفئة A.
فقط تخيل عدد المشاكل التي يمكن أن يسببها هذا نظريًا فارق بسيط. خاصة إذا كان المشروع ضخمًا ، يتم إنشاؤه تلقائيًا ولا تعرف في أي فئات يتم الإعلان عن هذا المتغير.
حل المشكلات
في وقت سابق من المقالة ، قيل أن Objective-C سيعطي السبق لـ C # Reflection. على وجه التحديد ، لحل مشكلتنا ، يمكنك استخدام الآلية المسماة
Method Swizzling . جوهر هذه الآلية هو أن لدينا الفرصة لاستبدال تنفيذ طريقة من أي فئة بأخرى أثناء التطبيق. وبالتالي ، يمكننا استبدال طريقة الاهتمام في UnityAppController بأخرى مخصصة. نأخذ التنفيذ الحالي ونكمل الشفرة التي نحتاجها. نحن نكتب رمزًا يستبدل التطبيق الحالي للطريقة بالطريقة التي نحتاجها. أثناء عمل التطبيق ، سيعمل المفوض الذي يستخدم الماكرو كما كان من قبل ، مستدعيًا التنفيذ الأساسي لـ UnityAppController ، وهناك طريقة مخصصة لدينا ستدخل حيز التنفيذ وسنحقق النتيجة المرجوة. هذا النهج مكتوب بشكل جيد وموضح في
هذه المقالة . باستخدام هذه التقنية ، يمكننا إنشاء فئة مساعدة - تناظرية لمندوب مخصص. , (Wrapper) . , , , , .
, , — , . , , . , , , , , .
, , Unity3D, -. :
- , , SOLID .
- UnityAppController XCode . UnityAppController .
- UnityAppController Unity .
- XCode UnityAppController ,
أصعب عنصر في هذه القائمة هو بلا شك العنصر الأخير. ومع ذلك ، يمكن تنفيذ هذه الميزة في Unity3D باستخدام البرنامج النصي للبناء بعد المعالجة. تمت كتابة مثل هذا السيناريو ليلة واحدة جميلة ، يمكنك مشاهدته على GitHub .هذه العملية اللاحقة سهلة الاستخدام ، اخترها في مشروع الوحدة. انظر في نافذة المفتش وشاهد حقلاً يسمى NewDelegateFile. اسحب برنامج UnityAppController المعدل وأفلته في هذا الحقل واحفظه.
iOS , . , , Unity UnityAppController'a.
ملاحظة
, , . , .