مقدمة: لماذا نحلل أداء كود Swift؟
\nتطوير تطبيقات iOS و macOS لا يتطلب فقط الوظائف، بل أيضًا أداءً عاليًا. حتى أجمل كود قد يكون بطيئًا، أو يستهلك ذاكرة زائدة، أو يستنزف البطارية. تحليل الأداء هو عملية قياس أداء تطبيقك، مما يساعد في اكتشاف نقاط الاختناق (bottlenecks). في هذه المقالة، سنستعرض طرقًا عملية لتحليل أداء كود Swift باستخدام الأدوات المدمجة في Xcode ونتعلم كيفية تفسير النتائج.
\n\n1. أدوات تحليل الأداء في Xcode: Instruments
\nالأداة الرئيسية لتحليل أداء تطبيقات Swift هي Instruments. وهي مدمجة في Xcode وتوفر العديد من القوالب للتحليل.
\n\n1.1. بدء تحليل الأداء
\nلبدء التحليل، اختر في Xcode: Product → Profile (أو اضغط Cmd + I). ستفتح نافذة اختيار القالب. للبحث عن نقاط الاختناق، الأكثر استخدامًا هي:
- \n
- Time Profiler — تحليل استخدام المعالج (CPU). \n
- Allocations — تتبع تخصيص الذاكرة. \n
- Leaks — البحث عن تسربات الذاكرة. \n
- Core Animation — تحليل أداء الرسومات. \n
1.2. مثال: إيجاد دالة "ثقيلة"
\nلنفترض أن لدينا دالة تعالج مصفوفة بيانات:
\nfunc processItems(_ items: [Int]) -> [Int] {\n var result = [Int]()\n for item in items {\n // محاكاة عملية ثقيلة\n usleep(100) // 0.1 مللي ثانية\n result.append(item * 2)\n }\n return result\n}\nشغّل Time Profiler وانتظر بضع ثوانٍ. في نافذة Instruments، سنرى أن الدالة processItems تستهلك 95% من الوقت. هذه نقطة اختناق واضحة. الحل: تسريع المعالجة (مثل استخدام DispatchQueue.concurrentPerform أو تحسين الخوارزمية).
2. تحليل وقت التنفيذ (Time Profiler)
\nTime Profiler هو الأداة الأكثر شيوعًا. يُظهر أي الدوال تستهلك أكبر قدر من وقت المعالج.
\n\n2.1. قراءة النتائج
\nبعد التحليل، سترى شجرة الاستدعاءات (Call Tree). كلما كان الشريط بجانب الدالة أوسع، زاد الوقت الذي استغرقته. انتبه إلى:
\n- \n
- Self — الوقت المستغرق مباشرة في هذه الدالة (بدون احتساب الدوال التي تستدعيها). \n
- Total — الوقت الإجمالي مع جميع الاستدعاءات الداخلية. \n
- Weight — النسبة المئوية من الوقت الإجمالي. \n
2.2. مثال عملي: البحث عن "تجميد" واجهة المستخدم
\nلنفترض أن واجهة المستخدم تتجمد عند التمرير في جدول. شغّل Time Profiler وابحث عن الدالة tableView(_:cellForRowAt:). إذا كانت تستهلك أكثر من 10% من الوقت، فهذا يعني أنك تقوم بعمل زائد في الخيط الرئيسي. التحسين:
// سيء: تحميل الصورة بشكل متزامن في الخيط الرئيسي\nfunc tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {\n let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)\n let imageData = try! Data(contentsOf: url) // يمنع واجهة المستخدم!\n cell.imageView?.image = UIImage(data: imageData)\n return cell\n}\n\n// جيد: تحميل غير متزامن\nfunc tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {\n let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)\n DispatchQueue.global().async {\n if let imageData = try? Data(contentsOf: url) {\n DispatchQueue.main.async {\n cell.imageView?.image = UIImage(data: imageData)\n }\n }\n }\n return cell\n}\n\n3. تحليل الذاكرة (Allocations و Leaks)
\nتسربات الذاكرة مشكلة شائعة في Swift بسبب الإغلاقات (closures) والمراجع الدائرية. تساعد Instruments في اكتشافها.
\n\n