مقدمة: لماذا تحليل أداء كود Swift هو المهارة رقم 1 لمطور iOS/macOS
أصبحت تطبيقات iOS و macOS الحديثة أكثر تعقيدًا، وأصبح المستخدمون أكثر تطلبًا. لا أحد سينتظر 5 ثوانٍ لتحميل شاشة، أو يتحمل رسومًا متحركة متقطعة. تحسين الأداء ليس رفاهية، بل ضرورة. ولكن كيف تعرف أي جزء من الكود يبطئ التطبيق بأكمله؟ الإجابة هي تحليل أداء كود Swift.
تحليل الأداء هو عملية جمع بيانات حول تشغيل البرنامج: وقت تنفيذ الدوال، استخدام الذاكرة، عدد مرات الاستدعاء. بدونها، تتحول أي محاولات للتحسين إلى تخمين. في هذه المقالة، سنستعرض الأدوات والتقنيات العملية التي ستساعدك في إيجاد نقاط الاختناق في كود Swift لتطبيقات iOS و macOS. ستتعلم استخدام Instruments و os_signpost والأدوات المدمجة في Xcode، وسترى أمثلة حقيقية على الكود.
1. أدوات تحليل أداء Swift في Xcode
يوفر Xcode مجموعة قوية من أدوات تحليل الأداء، مجمعة في تطبيق Instruments. إليك أهمها التي ستفيد كل مطور:
- Time Profiler — يوضح مقدار وقت المعالج المستهلك في كل دالة. مثالي للبحث عن "النقاط الساخنة" (hot spots).
- Allocations — يتتبع تخصيص وتحرير الذاكرة. يساعد في إيجاد التسريبات والاستهلاك الزائد.
- Leaks — يبحث تلقائيًا عن تسريبات الذاكرة (retain cycles).
- Core Animation — يحلل أداء الرسومات والرسوم المتحركة.
- os_signpost — واجهة برمجة تطبيقات (API) لإضافة علامات يدويًا في الكود، والتي تظهر لاحقًا في Instruments.
بدء تحليل الأداء سهل: اختر المخطط (Scheme) في Xcode، اضغط Cmd + I (Product → Profile)، واختر القالب المناسب في Instruments.
1.1. التشغيل الأول: Time Profiler
لنفترض أن لديك تطبيقًا يعالج ببطء قائمة من 10,000 عنصر. قم بتشغيل Time Profiler ونفذ السيناريو. بعد بضع ثوانٍ، سترى مكدس الاستدعاءات مرتبًا حسب الوقت. إليك كيف يبدو ذلك في الكود:
func processLargeArray() { let data = (0..<10000).map { $0 } let result = data.filter { $0 % 2 == 0 }.map { $0 * 2 } print(result.count)}سيظهر Time Profiler أن filter و map يستهلكان معظم الوقت. لكن هذا متوقع. ماذا لو كانت المشكلة في شيء آخر؟ على سبيل المثال، استدعاء متكرر لدالة مكلفة داخل حلقة.
2. مثال عملي: تحسين العمل مع Core Data
إحدى المشكلات الشائعة في iOS هي الاستعلامات البطيئة لـ Core Data. لنأخذ مثالاً: تحميل 5000 كائن مع تصفية حسب الاسم.
func fetchUsers(with name: String) -> [User] { let request = User.fetchRequest() request.predicate = NSPredicate(format: "name == %@", name) request.sortDescriptors = [NSSortDescriptor(key: "name", ascending: true)] return (try? context.fetch(request)) ?? []}بتشغيل Time Profiler، قد ترى أن عملية الجلب (fetch) تستغرق 200 مللي ثانية — وهو كثير. لكن لماذا؟ ربما لم يتم إنشاء فهرس (index) على الحقل name. في Xcode، أضف الفهرس في نموذج البيانات:
- افتح ملف .xcdatamodeld.
- اختر الكيان
User. - في مفتش البيانات (Data Model Inspector)، أضف
nameفي قسم Indexes.
بعد ذلك، سيظهر التحليل المتكرر انخفاضًا في الوقت إلى 20–30 مللي ثانية. الفرق 10 مرات!
2.1. استخدام os_signpost للترميز اليدوي
أحيانًا لا توفر الأدوات القياسية التفاصيل المطلوبة. هنا يأتي دور os_signpost. يتيح لك تحديد بداية ونهاية الجزء المطلوب من الكود يدويًا، ثم رؤية هذه العلامات في Instruments.