في
مقال سابق ، علمنا بأسباب عدم ثبات اختبارات الوحدة وكيفية التعامل معها. الآن نريد أن نفكر في واحدة من أدوات Apple الجديدة لتصحيح الأخطاء وتنميتها. نحن نتحدث عن إطار تسجيل الدخول os_log المقدم في WWDC 2018 ، والذي تم توسيعه بواسطة أداة تحليل الأداء ، os_signpost.

في أحد السباقات ، تم تكليفنا بتنفيذ عملية إنشاء وثيقة pdf على جانب العميل. أكملنا المهمة وأظهرنا النتائج بنجاح للفريق. لكننا أردنا التأكد من فعالية الفروق الفنية للقرار. ساعدنا Signpost بهذا. باستخدامه ، تمكنا من تسريع عرض المستند عدة مرات.
لمعرفة المزيد حول تقنية تطبيق os_signpost ، راجع أين يمكن أن تساعدك وكيف ساعدتنا بالفعل ، انتقل إلى أسفل القط.
أعمق في هذه القضية
هناك العديد من التطبيقات على هاتف المستخدم ، وجميعها تستخدم موارد النظام العامة: وحدة المعالجة المركزية ، ذاكرة الوصول العشوائي ، الشبكة ، البطارية ، إلخ. إذا كان التطبيق الخاص بك يؤدي مهامه ولم يتعطل ، فهذا لا يعني أنه يعمل بكفاءة وبشكل صحيح. فيما يلي وصف للحالات التي قد تواجهها.
خوارزمية دون المستوى الأمثل يمكن أن يؤدي إلى تحميل وحدة المعالجة المركزية طويلة.- في بداية التطبيق ، بعد 20 ثانية من الانتظار ، سيقوم النظام بإغلاق التطبيق ، ولن يرى المستخدم حتى الشاشة الأولى. في هذه الحالة ، سيقوم النظام بتعيين تقرير تعطل ، ستكون الميزة المميزة منه نوع استثناء - EXC_CRASH (SIGKILL) بنوع 0x8badf00d .
- يمكن أن تؤثر العمليات كثيفة الاستخدام للموارد في مؤشر ترابط الخلفية على استجابة واجهة المستخدم ، وزيادة استهلاك البطارية وإجبار التطبيق على إنهاء النظام (في حالة ارتفاع درجة حرارة وحدة المعالجة المركزية لفترات طويلة).
حالات RAM:لا توفر مواصفات الهواتف على موقع Apple معلومات عن ذاكرة الوصول العشوائي ، ولكن هناك مصادر أخرى توفر تخصيص الذاكرة التالي لطرز الهواتف:
اكتب
| 4S
| 5
| 5C
| 5S
| 6
| 6P
| 6S
| 6SP
|
ذاكرة الوصول العشوائي ، جيجابايت
| 0.5
| 1
| 1
| 1
| 1
| 1
| 2
| 2
|
اكتب
| SE
| اكس
| 7
| 7P
| 8
| 8P
| XS
| XSM
| Xr
|
ذاكرة الوصول العشوائي ، جيجابايت
| 2
| 3
| 2
| 3
| 2
| 3
| 4
| 4
| 3
|
عندما يكون هناك ذاكرة RAM منخفضة جدًا ، يبدأ iOS في البحث عن الذاكرة لتحريرها ، في الوقت نفسه بإرسال تحذير بالذاكرة إلى جميع التطبيقات قيد التشغيل. تؤثر هذه العملية ضمنيًا على وحدة المعالجة المركزية وبطارية الجهاز. في حالة تجاهل تحذير الذاكرة واستمرار تخصيص الذاكرة ، يقوم النظام بإنهاء عملية التطبيق بالقوة. بالنسبة للمستخدم ، يبدو هذا وكأنه تعطل ، مع عدم وجود backtraces في تقرير التعطل.
الاستخدام المفرط لطلبات الشبكة . هذا يؤدي أيضا إلى انخفاض في عمر البطارية. يؤدي تكرار الطلبات و / أو عدم إلغاء الطلبات غير الضرورية إلى الاستخدام غير الفعال لوحدة المعالجة المركزية.
لا تنسى موقع CoreLocation . كلما طلبنا أكثر وأكثر دقة لموقع المستخدم ، زادت إنفاق بطارية الجهاز. للتحقق من صحة معالجة الحالات الموصوفة ، نقترح استخدام os_signpost لملف تعريف عمليات التطبيق ثم تحليل البيانات التي تم الحصول عليها.
أداة التكامل في المشروع
في المستوى الأعلى ، تتكون عملية إنشاء PDF من ثلاث خطوات:
- تلقي البيانات عبر الشبكة ؛
- تشكيل الوثائق
- عرض على الشاشة - قررنا تقسيم وتسجيل مراحل إنشاء المستندات ، بدءًا من قيام المستخدم بالنقر فوق الزر "إنشاء" وتنتهي بعرض المستند على الشاشة.
افترض أننا نواجه مهمة تحليل طلب شبكة غير متزامن. ستبدو العلامات في الكود كما يلي:
import os.signpost let pointsOfInterestLog = OSLog(subsystem: "com.example.your-app", category: . pointsOfInterest) let networkLog = OSLog(subsystem: "com.example.your-app", category: "NetworkOperations") os_signpost(.event, log: pointsOfInterestLog, name: "Start work") os_signpost(.begin, log: networkLog, name: "Overall work") for element in elements { os_signpost(.begin, log: networkLog, name: "Element work") makeWork(for: element) os_signpost(.end, log: networkLog, name: "Element work") } os_signpost(.end, log: networkLog, name: "Overall work")
الخطوات لاستخدام signpost كالتالي:
- استيراد إطار os.signpost.
- إنشاء مثيل OSLog. تجدر الإشارة إلى أن هناك عدة أنواع من الأحداث: بالنسبة للأحداث الفاصلة (على سبيل المثال ، طلب شبكة) ، يمكنك استخدام فئة اعتباطية ، وللأحداث المتزامنة (على سبيل المثال ، النقر فوق زر) ، ونقاط الفئة المحددة مسبقًاOfInterest / OS_LOG_CATEGORY_POINTS_OF_INTEREST.
- بالنسبة إلى أحداث الفاصل الزمني ، اتصل بوظيفة os_signpost بنوع .begin و .end في بداية ونهاية المرحلة قيد التحقيق. للأحداث المتزامنة ، استخدم نوع .vent.
- إذا كان يمكن تنفيذ التعليمات البرمجية قيد التحقيق بشكل غير متزامن ، فقم بإضافة معرف Signpost ، والذي سيتيح لك فصل الفواصل الزمنية لنفس النوع من العمليات بكائنات مختلفة.
- اختياريا ، يمكنك إضافة بيانات إضافية (بيانات التعريف) إلى الأحداث المرسلة. على سبيل المثال ، حجم الصور التي تم تنزيلها عبر الشبكة أو رقم صفحة PDF التي تم إنشاؤها. ستساعد هذه المعلومات في فهم ما يحدث بالضبط في المرحلة التي تم التحقيق فيها من تنفيذ التعليمات البرمجية.
بالمثل على obj-c:
@import os.signpost; os_log_t pointsOfInterestLog = os_log_create("com.example.your-app", OS_LOG_CATEGORY_POINTS_OF_INTEREST); os_log_t networkLog = os_log_create("com.example.your-app", "NetworkOperations"); os_signpost_id_t operationIdentifier = os_signpost_id_generate(networkLog); os_signpost_event_emit(pointsOfInterestLog, operationIdentifier, "Start work"); os_signpost_interval_begin(networkLog, operationIdentifier, "Overall work"); for element in elements { os_signpost_id_t elementIdentifier = os_signpost_id_make_with_pointer(networkLog, element); os_signpost_interval_begin(networkLog, elementIdentifier, "Element work"); [element makeWork]; os_signpost_interval_end(networkLog, elementIdentifier, "Element work"); } os_signpost_interval_end(networkLog, operationIdentifier, "Overall work");
إلى ملاحظة. إذا كان يجب تشغيل المشروع على نظام التشغيل iOS قبل الإصدار 12.0 ، فسوف يعرض Xcode التفاف مكالمات os_signpost في بنية if #available. لكي لا تشوش الكود ، يمكنك وضع هذا المنطق في فصل منفصل.
تجدر الإشارة إلى أن os_signpost يتطلب سلسلة ثابتة ثابتة كمعلمة لاسم الحدث. لإضافة كتابة أكثر صرامة ، يمكنك إنشاء تعداد مع أنواع الأحداث ، وفي تطبيق الفصل ، قم بتعيينها على سلسلة حرفية. سيؤدي وضع OSLog في فصل منفصل إلى إضافة المنطق لتعطيله لنظام الإصدار (هناك أمر OSLog منفصل لهذا).
import os.signpost let networkLog: OSLog if ProcessInfo.processInfo.environment.keys.contains("SIGNPOSTS_FOR_NETWORK") { networkLog = OSLog(subsystem: "com.example.your-app", category: "NetworkOperations" } else { networkLog = .disabled }
يمكنك إضافة قيم من أي خصائص إلى حدث الوسم باستخدام وحدات فك ترميز النوع التالية لتنسيق مناسب:
نوع القيمة
| محدد المواصفات
| مثال الإخراج
|
time_t
| ٪ {time_t} د
| 2016-01-12 19:41:37
|
timeval
| ٪ {timeval}. * P
| 2016-01-12 19: 41: 37.774236
|
timespec
| ٪ {timespec}. * P
| 2016-01-12 19: 41: 37.2382382823
|
errno
| ٪ {errno} د
| أنبوب مكسور
|
iec بايت
| ٪ {iec-bytes} d
| 2.64 ميج
|
معدل البت
| ٪ {bitrate} d
| 123 كيلو بايت في الثانية
|
معدل البت اي
| ٪ {iec-bitrate} d
| 118 كيلوبت في الثانية
|
uuid_t
| ٪ {uuid_t}. * 16P ٪ {uuid_t}. * P
| 10742E39-0657-41F8-AB99-878C5EC2DCAA
|
الآن ، عند تعيين التطبيق ، سيتم إرسال الأحداث من os_signpost إلى الأدوات في شكل بيانات جدولية. للتبديل إلى الأدوات ، استخدم اختصار لوحة المفاتيح Cmd + I ، ثم حدد الأداة اللازمة للتوصيف. لمشاهدة البيانات المميزة ، ما عليك سوى تشغيل أدوات os_signpost و Point of Interest على الجانب الأيمن من واجهة الأداة.

بشكل افتراضي ، يتم تجميع الأحداث في فئات وعرضها في جدول ، حيث يتم حساب عددهم وإحصائياتهم في وقت التشغيل. بالإضافة إلى ذلك ، هناك عرض رسومي على الخط الزمني ، مما يجعل من السهل مقارنة الأحداث المستلمة بالنتائج في الأدوات الأخرى. هناك أيضًا إمكانية تخصيص عرض الإحصائيات وكتابة أنظمة الخبراء - ولكن هذا الموضوع يستحق مقالة منفصلة.
أمثلة الاستخدام
القضية رقم 1. PDFKit مقابل WKWebView
من خلال استخدام os_signpost ، رأينا أنه بالنسبة للمستندات الصغيرة (بضع صفحات) كانت أطول خطوة هي الخطوة الأخيرة - عرض المستند - بدلاً من العمل مع شبكة أو رسومات. أدى ذلك بنا إلى قرار استبدال
WKWebView بـ
PDFView ، مما أدى إلى تسريع عرض المستند من 1.5 ثانية إلى 30 مللي ثانية. على الرسوم البيانية ، يبدو كما يلي:
عرض وثيقة PDF (WKWebView) في Time Profiler
عرض وثيقة PDF (PDFView) في Time Profilerيمكن تنفيذ البيانات الناتجة في أدوات أخرى يوفرها Xcode. كما أظهرت أداة التخصيص ، تم تحقيق زيادة في سرعة التنزيل من خلال زيادة استخدام ذاكرة الوصول العشوائي.
القضية رقم 2. تحذير LowMemory
يتم إنشاء مستند PDF بشكل غير متزامن ، ويتطلب تكوينه تخصيص مقدار كبير من الذاكرة. في حالة عدم كفاية الذاكرة ، قررنا إضافة القدرة على إيقاف العملية غير المتزامنة لإنشاء مستند.
كما تعلمون ، عند استخدام NSOperationQueue ، فإن أسلوب CancelAllOperation يحرر قائمة انتظار موجودة ، لكنه لا يتوقف عن تشغيل العمليات بالفعل. من هذا نستنتج أنه في تنفيذ العملية من الضروري تحديد حالتها بشكل دوري والتوقف عن العمل. وبالتالي تحرير الموارد إذا تم تعيينه إلى الحالة الملغاة.
الخطوة التالية هي عملية غير متزامنة نحتاجها للتحقق من الإلغاء. ولكن في الوقت نفسه ، ليس من الواضح مع ما تردد القيام بهذا الاختيار. كان لدينا خياران - التحقق من سطر بسطر والتحقق من صفحة إلى أخرى. ساعد os_signpost هنا أيضا. كما اتضح ، مع إضافة فحص للإلغاء في دورة سطرا لجعل الجدول في المستند ، قمنا بزيادة الوقت الذي يستغرقه إنشاء المستند (بمقدار 150 صفحة) بمعدل 2 مرات. كان الخيار الثاني هو الأمثل من حيث الأداء ولم يزيد في الواقع من الوقت الذي استغرقه إنشاء المستند. نتيجة لذلك ، عندما نتلقى حدث تحذير الذاكرة ، نقوم بإلغاء العملية برمجياً ونعرض شاشة الخطأ للمستخدم.
للتأكد من تحرير الذاكرة بالفعل ، يمكننا أيضًا استخدام os_signpost. هذه المرة عن طريق إضافة علامة حول بداية الحدث في أسلوب didRecieveMemoryWarning وعلامة حول النهاية في viewDidLoad لشاشة الخطأ. بالمناسبة ، يمكنك محاكاة حدث ذاكرة غير كافٍ في جهاز المحاكاة (shift + command + m).
القضية رقم 3. قيود التحديث
قد تكون الإشارة مفيدة في عملية التخطيط. لإنشاء قيود ، نستخدم إطار
البناء . تشير وثائق الإطار إلى أنه يوصى باستخدام طريقة updateConstrict () لإنشاء المنشورات. لكن Apple لا تشجع بشدة القيام بذلك ، ويمكنك التحقق من ذلك باستخدام علامة signpost.

وفقًا لوثائق Apple ، يجب استخدام updateConstrict فقط لتغيير القيود إذا لم نتمكن من القيام بذلك في المكان الذي حدث فيه التغيير.

بعد تحليل النتائج ، استنتجنا أن مكالمة updateConstrict في تطبيقنا ليست متكررة جدًا - تقريبًا في كل مرة يظهر فيها العرض على الشاشة.
على الرغم من ذلك ، لتجنب عيوب الأداء المحتملة ، نوصي باتباع نصيحة Apple في هذا الصدد.
النتائج
في عام 2018 ، وفرت شركة Apple للمطورين الفرصة لتوسيع أدوات التنميط بشكل مستقل. بالطبع ، يمكنك استخدام أدوات تصحيح الأخطاء الأخرى: نقاط التوقف ، الإخراج إلى وحدة التحكم ، أجهزة ضبط الوقت ، ملفات التعريف المخصصة. لكنهم يحتاجون إلى مزيد من الوقت لتنفيذ أو لا يقدمون دائمًا صورة كاملة لما يحدث.
في المقالة التالية ، سننظر في كيفية استخدام المعلومات الواردة من اللافتة بشكل أكثر كفاءة من خلال كتابة نظامنا الخبير (الأدوات المخصصة).
روابط مفيدةكُتب المقال مع victoriaqb - Victoria Kashlina ، مطور iOS.