معالجة الأخطاء في Swift: try/catch وما بعدها — دليل شامل
معالجة الأخطاء هي أحد الموضوعات الأساسية في التطوير لنظامي iOS و macOS. في لغة Swift، تم تنفيذ نظام قوي وأنيق لإدارة الأعطال يتجاوز بكثير مجرد try/catch البسيط. فهم هذه الآليات يسمح بكتابة كود موثوق وقابل للتنبؤ وآمن. في هذه المقالة، سنقوم بتحليل جميع الجوانب: من الإعلان الأساسي عن الأخطاء إلى التقنيات المتقدمة مع القيم الاختيارية ومدمرات الكائنات (deinitializers).
الأساسيات: بروتوكول Error والكلمة المفتاحية throws
في Swift، يجب أن يتوافق أي خطأ مع بروتوكول Error. عادةً ما يتم استخدام التعدادات (enum) لهذا الغرض. لنلقِ نظرة على مثال بسيط — التحقق من كلمة المرور:
enum PasswordError: Error { case tooShort case missingDigit case missingUppercase}
func validatePassword(_ password: String) throws { guard password.count >= 8 else { throw PasswordError.tooShort } guard password.rangeOfCharacter(from: .decimalDigits) != nil else { throw PasswordError.missingDigit } guard password.rangeOfCharacter(from: .uppercaseLetters) != nil else { throw PasswordError.missingUppercase } print("كلمة المرور صحيحة")}الكلمة المفتاحية throws في إعلان الدالة تُخبر المترجم بأن التنفيذ قد يتوقف بسبب خطأ. استدعاء مثل هذه الدالة يتطلب صياغة خاصة.
بناء do-catch والكلمة المفتاحية try
لمعالجة الأخطاء المحتملة، يتم استخدام كتلة do-catch مع وضع علامة try قبل الاستدعاء:
do { try validatePassword("Hello123") print("نجاح!")} catch PasswordError.tooShort { print("خطأ: كلمة المرور قصيرة جداً")} catch PasswordError.missingDigit { print("خطأ: كلمة المرور لا تحتوي على رقم")} catch PasswordError.missingUppercase { print("خطأ: كلمة المرور لا تحتوي على حرف كبير")} catch { print("خطأ غير معروف: \\(error)")}لاحظ كتلة catch الأخيرة — فهي تلتقط أي أخطاء غير متوقعة سابقاً. هذا مطلب إلزامي: يتحقق مترجم Swift من معالجة جميع الأخطاء المحتملة.
try? و try! — صيغ مبسطة للمعالجة
في بعض الأحيان لا تحتاج إلى تحكم كامل في الأخطاء. يقدم Swift صيغتين مختصرتين:
- try? — يحول الخطأ إلى قيمة اختيارية
nil. إذا لم يكن هناك خطأ، فإنه يُرجع النتيجة مغلفة فيOptional. - try! — يؤكد عدم وجود خطأ. إذا حدث خطأ، فسينهار التطبيق بشكل غير متوقع (runtime crash).
// try? — تجاهل آمن للخطأif let _ = try? validatePassword("weak") { print("كلمة المرور اجتازت التحقق")} else { print("كلمة المرور لم تجتاز")}
// try! — فقط إذا كنت متأكداً 100%let result = try! validatePassword("StrongPass1") // انهيار إذا كانت كلمة المرور غير صالحةاستخدم try! فقط في المواقف التي يكون فيها الخطأ مستحيلاً نظرياً (على سبيل المثال، عند تحميل مورد مضمن في حزمة التطبيق).
تقنيات متقدمة: rethrows و defer و Result
إعادة رمي الأخطاء (rethrows)
الكلمة المفتاحية rethrows تُطبق على الدوال عالية المستوى التي تستقبل إغلاقاً (closure) قادراً على رمي خطأ. الدالة نفسها لا ترمي خطأ، ولكنها تنقله من الإغلاق:
func processItems(_ items: [String], using validator: (String) throws -> Void) rethrows { for item in items { try validator(item) }}
// الاستخدامdo { try processItems(["Hello", "World123"], using: validatePassword)} catch { print("خطأ في المعالجة: \\(error)")}هذا يسمح بكتابة دوال تغليف مرنة دون تكرار كود معالجة الأخطاء.