الوصول الآمن إلى المصفوفات في Swift: كيف تنسى مشكلة index out of range

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

مقدمة: لماذا قد ينهار كودك في أي لحظة؟

\n

كل مطور Swift واجه مرة على الأقل هذا الخطأ الشبح: Fatal error: Index out of range. يظهر فجأة، يدمر التطبيق، ويرى المستخدم شاشة بيضاء فقط. قد يبدو الأمر بسيطًا، أخذ عنصر من مصفوفة باستخدام الفهرس؟ لكن في المشاريع الحقيقية - خاصة عند العمل مع بيانات الشبكة، تحليل JSON، أو إدخال المستخدم - يمكن للفهرس أن يتجاوز الحدود بسهولة.

\n

في هذه المقالة، سنشرح كيف تنسى مشكلة index out of range إلى الأبد وتجعل كودك ليس آمنًا فحسب، بل أنيقًا أيضًا. سنتحدث عن safe array lookup في Swift، ونستخدم optional bindings (if let و guard let)، ونتعلم كتابة إضافاتنا الخاصة، ونرى كيف يفعل المحترفون ذلك في بيئة الإنتاج.

\n\n

المشكلة: الوصول القياسي إلى المصفوفة - لعبة الروليت الروسية

\n

لنلق نظرة على موقف نموذجي. لديك مصفوفة من السلاسل النصية:

\n
let fruits = ["Яблоко", "Банан", "Апельсин"]\nlet index = 3\nprint(fruits[index]) // 💥 Fatal error: Index out of range
\n

الكود سينهار فورًا. Swift هي لغة ذات كتابة آمنة، لكن الـ subscript القياسي ([]) لا يتحقق من الحدود. إنه ببساطة يأخذ العنصر من عنوان الذاكرة، وإذا كان الفهرس غير صحيح - ينهار التطبيق. هذا إرث من C و Objective-C الذي نريد التخلص منه.

\n\n

الحل رقم 1: Optional Binding (if let) - الخطوة الأولى نحو الأمان

\n

الطريقة الأكثر وضوحًا هي التحقق من الفهرس يدويًا قبل الوصول. لكن Swift يقدم طريقة أكثر اصطلاحية: استخدام optional binding مع التحقق من النطاق.

\n

هذا ما يبدو عليه:

\n
let fruits = ["Яблоко", "Банан", "Апельсин"]\nlet index = 2\n\nif index < fruits.count {\n    let fruit = fruits[index]\n    print("Мы получили: \\(fruit)")\n} else {\n    print("Индекс \\(index) вне диапазона")\n}
\n

هذا يعمل، لكنه يبدو ضخمًا. ماذا لو أردنا فعل ذلك في سطر واحد والحصول على قيمة اختيارية؟ هنا يأتي دور guard:

\n
func safeFruit(at index: Int) -> String? {\n    guard index >= 0, index < fruits.count else { return nil }\n    return fruits[index]\n}\n\nif let fruit = safeFruit(at: 3) {\n    print("Ура, мы получили фрукт: \\(fruit)")\n} else {\n    print("Фрукта по такому индексу нет")\n}
\n

هذا أفضل! الدالة تُرجع Optional<String>، ونحن نتعامل مع nil بهدوء. لا انهيارات. لكن كتابة دالة كهذه لكل مصفوفة - متعة مشكوك فيها.

\n\n

الحل رقم 2: الإضافة (Extension) - سحر البروتوكولات

\n

القوة الحقيقية لـ Swift تكمن في الإضافات. يمكننا إضافة subscript آمن لأي مصفوفة مرة واحدة واستخدامه في كل مكان. إليك كيف يتم ذلك:

\n
extension Collection {\n    /// يعيد بأمان العنصر حسب الفهرس إذا كان موجودًا.\n    /// - Parameter index: فهرس العنصر.\n    /// - Returns: عنصر اختياري.\n    subscript(safe index: Index) -> Element? {\n        return indices.contains(index) ? self[index] : nil\n    }\n}
\n

الآن يمكننا الكتابة هكذا:

\n
let numbers = [10, 20, 30, 40, 50]\n\nif let first = numbers[safe: 0] {\n    print("Первый элемент: \\(first)") // 10\n}\n\nif let last = numbers[safe: 4] {\n    print("Последний элемент: \\(last)") // 50\n}\n\nif let nonExistent = numbers[safe: 10] {\n    print("Это никогда не выведется")\n} else {\n    print("Элемента с индексом 10 не существует")\n}
\n

لاحظ: لقد قمنا بتوسيع بروتوكول Collection، وليس النوع المحدد Array. هذا يعني أن الـ subscript الآمن الخاص بنا يعمل مع جميع المجموعات: المصفوفات، السلاسل النصية، DictionaryKeys، Set، وحتى الأنواع المخصصة التي تتوافق مع Collection.

\n\n

لماذا يعمل هذا؟

\n
    \n

المدونات

توصيات الكتب