أداء Swift Beta: فرز المصفوفات — ما الخطأ الذي حدث؟
\n\nإذا كنت قد ألقيت نظرة على Stack Overflow في عصر الإصدارات التجريبية الأولى من Swift، فبالتأكيد تتذكر السؤال الأسطوري الذي جمع آلاف المشاهدات ومئات التعليقات. كان نصه تقريبًا: «لماذا يعمل فرز المصفوفات في Swift Beta أبطأ بمقدار 100 مرة من Objective-C؟»
\n\nفي ذلك الوقت، في عام 2014، كانت هذه صدمة حقيقية لمجتمع iOS. المطورون الذين اعتادوا على الفرز السريع للغاية في Objective-C، رأوا فجأة تطبيقاتهم تتجمد دون سبب واضح. اليوم، بعد سنوات، يمكننا تحليل هذه المشكلة بهدوء: فهم ما الذي تسبب في البطء بالضبط، وكيف تطور Swift، والأهم من ذلك، كيفية فرز المصفوفات بشكل صحيح في Swift الحديث لتجنب تكرار أخطاء الماضي.
\n\nفي هذه المقالة، لن نعيد سرد الخطأ القديم فقط. سنقوم بإجراء اختباراتنا الخاصة، ونكتب الكود، ونكتشف أي خوارزميات الفرز في Swift سريعة حقًا، وأيها تمثل فخًا للمبتدئين. هيا بنا!
\n\nخلفية مختصرة: ماذا حدث في Swift Beta؟
\n\nفي الإصدار التجريبي الأول من Swift، كان فرز مصفوفة من 100,000 عدد صحيح قد يستغرق ما يصل إلى 20 ثانية. في Objective-C، كانت نفس العملية تتم في 0.2 ثانية. الفرق 100 مرة! كان السبب هو أن Swift المبكر استخدم تنفيذًا غير فعال لخوارزمية الفرز (Introsort مع اختيار سيئ للعنصر المحوري)، بالإضافة إلى مشاكل في التحسين عند الترجمة بدون أعلام التحسين (-O).
\n\nعلاوة على ذلك، في الإصدار التجريبي، غالبًا ما كان يتم تفعيل آلية التحقق الديناميكي من الحدود (bounds checking) لكل وصول إلى المصفوفة، مما أضاف تكاليف إضافية هائلة. كان الاستنتاج بسيطًا: Swift كان غير ناضج، لكن إمكاناته كانت هائلة.
\n\nكيفية فرز المصفوفات في Swift اليوم؟ أفضل الممارسات
\n\nSwift الحديث (الإصدار 5.x وما بعده) هو لغة ناضجة. أصبح فرز المصفوفات ليس سريعًا فحسب، بل واحدًا من أسرع الطرق بين اللغات الرئيسية. دعنا نحلل الطرق الأساسية.
\n\n1. الطريقة المدمجة sort() — المعيار الذهبي
\n\nأبسط وأفضل طريقة موصى بها هي استخدام طريقة sort() (الفرز في المكان) أو sorted() (إرجاع مصفوفة مرتبة جديدة).
import Foundation\n\nvar numbers = [5, 3, 8, 1, 9, 2]\nnumbers.sort() // فرز في المكان\nprint(numbers) // [1, 2, 3, 5, 8, 9]\n\nlet sortedNumbers = numbers.sorted() // إرجاع مصفوفة جديدة\nprint(sortedNumbers) // [1, 2, 3, 5, 8, 9]\n\nداخليًا، تستخدم sort() TimSort — خوارزمية هجينة تعمل بتعقيد O(n log n) في المتوسط و O(n) على البيانات شبه المرتبة. هذه هي نفس الخوارزمية المستخدمة في Python و Java. وهي محسّنة بشكل رائع للمعالجات الحديثة.
2. الفرز باستخدام الإغلاق: التخصيص
\n\nإذا كنت بحاجة إلى فرز مصفوفة تنازليًا أو وفقًا لمعيار معقد، استخدم الإغلاق:
\n\nlet words = ["تفاحة", "برتقال", "موز", "كيوي"]\nlet sortedByLength = words.sorted { $0.count < $1.count }\nprint(sortedByLength) // ["كيوي", "تفاحة", "موز", "برتقال"]\n\nlet descendingNumbers = numbers.sorted(by: >)\nprint(descendingNumbers) // [9, 8, 5, 3, 2, 1]\n\nهام: يجب أن يكون الإغلاق دالة نقية (pure function) — بدون آثار جانبية. وإلا فإنك تخاطر بسلوك غير متوقع وانخفاض في الأداء.
\n\n3. فرز الهياكل المخصصة
\n\nعند العمل مع أنواع البيانات الخاصة بك، من الأفضل تنفيذ بروتوكول Comparable. سيسمح ذلك باستخدام sort() بدون إغلاق.
struct Person: Comparable {\n let name: String\n let age: Int\n\n static func < (lhs: Person, rhs: Person) -> Bool {\n return lhs.age < rhs.age // الفرز حسب العمر\n }\n}\n\nle