Do-Catch: ما هو، البناء ومعالجة الأخطاء في Swift

المؤلف: IT Sectr نُشر: 2026-05-26 وقت القراءة: 8 دق

Do-Catch هو بناء في لغة Swift لمعالجة الأخطاء يلتقط الاستثناءات الملقاة عبر throw وينفذ كتلة التعليمات البرمجية المقابلة. على عكس try-catch التقليدي في اللغات الأخرى، تتطلب Swift وضع علامة صريحة على الوظائف التي يمكنها إلقاء خطأ باستخدام معدل throws. وفقًا لـ Swift.org، 2026، يعد Do-Catch الآلية الرئيسية لمعالجة الأخطاء في Swift، مما يوفر أمان الأنواع.

النقاط الرئيسية

  • Do-Catch هو بناء في Swift يلتقط الأخطاء من الكتل مع try و throw.
  • throws هو علامة وظيفة تشير إلى أنها يمكنها إلقاء خطأ.
  • try هي الكلمة المفتاحية لاستدعاء دالة throwing داخل كتلة do.
  • catch هو كتلة معالجة الأخطاء مع مطابقة الأنماط حسب النوع.
  • try? و try! هما صيغتان بديلتان تحولان الأخطاء إلى nil أو تسببان تعطلًا.

ما هو Do-Catch؟

Do-Catch هو بناء في Swift يتكون من كتلة do، يتم داخلها تنفيذ كود قادر على إلقاء خطأ، وكتلة أو أكثر من catch تعالج ذلك الخطأ. تحتوي كتلة do على استدعاءات للدوال throwing الموسومة بـ try، وتطابق كتل catch الخطأ حسب النوع.

ينفذ Swift نموذجًا لـ معالجة الأخطاء المنظمة، يختلف عن الاستثناءات في Objective-C. هنا الخطأ ليس استثناءً مع فك تكديس المكدس، بل قيمة تتوافق مع بروتوكول Error. يؤدي إلقاء خطأ عبر throw إلى نقل التحكم إلى أقرب كتلة catch دون عبء فك تكديس المكدس.

إحدى الميزات الرئيسية لـ Swift هي المعالجة الصريحة. لا يسمح المترجم باستدعاء دالة throwing بدون try أو do-catch أو آلية بديلة (try?, try!). هذا يلغي المواقف التي يمر فيها الخطأ دون ملاحظة.

نموذج الأخطاء في Swift: بروتوكول Error والدوال throwing

في قلب معالجة الأخطاء في Swift يكمن بروتوكول Error. أي نوع يتوافق مع هذا البروتوكول يمكن إلقاؤه عبر throw. يتم تعريف الأخطاء عادةً على أنها enum تتوافق مع Error.

بروتوكول Error

Error هو بروتوكول فارغ (علامة). لا يتطلب تنفيذ طرق، فقط التوافق. يستخدم مترجم Swift Error للتحقق الثابت: يمكن للدالة ذات throws فقط إلقاء قيمة تتوافق مع Error.

الدوال Throwing

يجب استدعاء دالة موسومة بـ throws مع try. إذا حدث throw داخل هذه الدالة، ينتقل التحكم إلى do-catch الذي استدعاها. إذا لم تتم معالجة الخطأ في أي مكان في سلسلة الاستدعاءات، فإنه ينتشر إلى المستوى الأعلى.

swift
enum FileError: Error {
    case notFound
    case permissionDenied
    case corrupted(String)
}

func readFile(path: String) throws -> String {
    guard FileManager.default.fileExists(atPath: path) else {
        throw FileError.notFound
    }
    return try String(contentsOfFile: path)
}

Rethrows

rethrows هو معدل للدوال التي تقبل إغلاقًا throwing كمعامل. إذا لم يلقِ الإغلاق الذي تم تمريره خطأ، يمكن استدعاء دالة rethrows بدون try. هذا يسمح بكتابة دوال عامة عالية الترتيب متوافقة مع كل من الإغلاقات throwing وغير throwing.

صيغة Do-Catch: كتل catch متعددة

يدعم Swift كتل catch متعددة مع مطابقة الأخطاء حسب النوع. هذا مشابه لـ when في Kotlin أو switch حسب النوع: أول catch يطابق يتم تنفيذه، ويتم تخطي الباقي.

المطابقة حسب النوع

يمكن أن تحتوي كل كتلة catch على نمط يطابق نوعًا محددًا من الخطأ. يتحقق Swift من الكتل بالترتيب، لذلك يجب أن تأتي الأنواع الأكثر تحديدًا قبل الأنواع العامة. إذا لم يطابق أي نمط، يتم تنفيذ كتلة catch بدون نمط.

معالجة أنواع متعددة

يسمح أمان الأنواع لـ Do-Catch بمعالجة كل خطأ على حدة أو دمج أنواع متعددة في كتلة واحدة عبر مطابقة الأنماط المفصولة بفواصل. هذا يلغي سلاسل if-else المرهقة النموذجية في Objective-C، حيث كان تحليل الأخطاء يتم عبر رموز الإرجاع أو NSError مع التحقق من المجال والرمز.

يدعم Swift أيضًا catch where — المطابقة مع شرط إضافي. على سبيل المثال، يمكنك تصفية الأخطاء حسب رمز أو نص محدد: catch URLError where error.code == .notConnectedToInternet. هذا يقلل من عدد كتل catch مع الحفاظ على الدقة.

لتصحيح الدوال throwing، يوفر Swift تمييزًا بين الأخطاء والاستثناءات. خطأ Swift هو قيمة إرجاع لا تتطلب فك تكديس المكدس. تعمل استثناءات Objective-C (@try/@catch) على مستوى runtime وتستخدم فقط للأعطال الفادحة. يعالج Do-Catch الأخطاء، وليس الاستثناءات، مما يجعله متوقعًا وفعالًا.

عند تطوير المكتبات وSDKs، من المهم تذكر أن الدوال throwing هي جزء من العقد العام. لا يتم تحديد نوع الخطأ في التوقيع، لذلك وثق الأخطاء التي يمكن أن تلقيها الدالة في التعليقات أو من خلال enum شبيه بـ Result. هذا يساعد مستخدمي API على كتابة كتل catch صحيحة دون الحاجة إلى النظر في التنفيذ.

في سياق البرمجة غير المتزامنة، يحل Do-Catch مع async/await مشكلة callback hell. سابقًا في Swift، كانت معالجة الأخطاء في الكود غير المتزامن تتطلب إغلاقات متداخلة مع التحقق من Error? في completion handlers. مع async/await و Do-Catch، يبدو الكود غير المتزامن throwing مثل الكود المتزامن، مما يبسط قراءة وصيانة سلاسل الطلبات المعقدة مع معالجة الأخطاء في كل خطوة.

swift
do {
    let content = try readFile(path: "/data/config.json")
    process(content)
} catch FileError.notFound {
    createDefaultConfig()
} catch FileError.permissionDenied {
    requestAccess()
} catch FileError.corrupted(let detail) {
    logCorruption(detail)
} catch {
    print("خطأ غير معروف: \(error)")
}

متغيرات try: try، try?، try! ومتى تستخدمها

يقدم Swift ثلاث طرق لاستدعاء الدوال throwing: try و try? و try!. كل خيار يحل مهمة محددة وله حدوده.

try — الاستدعاء القياسي

try يستخدم داخل كتلة do، ويتم معالجة الخطأ في catch. هذه هي الطريقة الأساسية لاستدعاء الدوال throwing. يتطلب المترجم أن يكون try في سياق حيث يمكن التقاط الخطأ.

try? — التحويل إلى Optional

try? يحول نتيجة دالة throwing إلى Optional. إذا ألقت الدالة خطأ، ترجع try? nil، وإلا ترجع Optional بالقيمة الناجحة. مفيد للاستدعاءات حيث يمكن تجاهل الخطأ ولكن هناك حاجة لمؤشر نجاح.

try! — التنفيذ القسري

try! يثبط معالجة الأخطاء. إذا ألقت الدالة throwing خطأ، ينتهي التطبيق بـ خطأ وقت التشغيل. استخدم try! فقط عندما تكون متأكدًا تمامًا من استحالة الخطأ — على سبيل المثال، عند تحميل مورد مدمج.

swift
// try? — نتجاهل الخطأ، نحصل على nil عند الفشل
if let data = try? Data(contentsOf: url) {
    processData(data)
}

// try! — فقط إذا كان الخطأ مضمون الاستحالة
let bundled = try! String(contentsOfFile: "Assets/default.txt")

// try — الخيار القياسي في do-catch
do {
    let result = try performNetworkRequest()
    updateUI(result)
} catch {
    showError(error)
}

Do-Catch في تطوير iOS: أمثلة عملية

يستخدم Do-Catch بنشاط في تطوير iOS للعمل مع APIs النظام. دعنا ننظر في السيناريوهات النموذجية: العمل مع نظام الملفات و Core Data وطلبات الشبكة.

نظام الملفات

العديد من طرق FileManager هي throwing. يسمح Do-Catch بمعالجة عدم وجود ملف أو انشغال المورد أو عدم كفاية الصلاحيات بشكل صحيح.

طلبات الشبكة مع Codable

تحليل JSON عبر JSONDecoder هو عملية throwing. يلتقط Do-Catch أخطاء فك الترميز وفشل الشبكة بشكل منفصل، مما يوفر للمستخدم رسالة خطأ دقيقة.

swift
struct User: Codable {
    let id: Int
    let name: String
}

func fetchUser(id: Int) async throws -> User {
    let url = URL(string: "https://api.example.com/users/\(id)")!
    let (data, _) = try await URLSession.shared.data(from: url)
    return try JSONDecoder().decode(User.self, from: data)
}

// استدعاء مع معالجة
do {
    let user = try await fetchUser(id: 42)
    showUser(user)
} catch let error as URLError {
    showNetworkAlert(error)
} catch let error as DecodingError {
    showParseError(error)
} catch {
    showGenericError(error)
}

الأخطاء الشائعة مع Do-Catch

غالبًا ما يرتكب المطورون، خاصة أولئك القادمون من لغات أخرى، أخطاء مميزة عند استخدام Do-Catch. دعنا ننظر إلى الأكثر شيوعًا.

  • try المنسي — استدعاء دالة throwing بدون try يسبب خطأ في الترجمة. لا يسمح Swift بتجاهل الأخطاء ضمنيًا.
  • catch فارغ — كتلة catch بدون معالجة تخفي الخطأ. دائمًا سجل أو تعامل مع الخطأ، حتى لو بدا مستحيلًا.
  • catch بدون مطابقة — catch عام واحد لجميع الأخطاء يفقد فوائد أمان الأنواع. افصل المعالجة حسب الأنواع باستخدام كتل متعددة.
  • الإفراط في استخدام try! — try! في كود الإنتاج هو مصدر أعطال غير متوقعة. استخدمه فقط للموارد المدمجة أو الاختبارات.

الأسئلة الشائعة

كيف يختلف Do-Catch في Swift عن try-catch في اللغات الأخرى؟

في Swift، الخطأ هو قيمة تتوافق مع بروتوكول Error، وليس استثناءً مع فك تكديس المكدس. يجب وضع علامة صريحة على الدوال throwing بـ throws، ويتطلب المترجم try عند الاستدعاء — هذا يلغي الأخطاء غير المعالجة في وقت الترجمة.

هل يمكن استخدام Do-Catch مع async/await؟

نعم، يمكن أن تكون الدوال async أيضًا throws، ويعمل do-catch معها. يتطلب استدعاء دالة async throwing try await داخل كتلة do. هذه هي الطريقة القياسية لمعالجة الأخطاء في كود Swift غير المتزامن.

كيف تتعامل مع خطأ بدون do-catch؟

هناك بدائل: try? يحول الخطأ إلى nil، try! يسبب تعطلًا عند الخطأ، و rethrows يسمح بنشر الخطأ من إغلاق. يبقى do-catch الطريقة الرئيسية للمعالجة الصريحة.

هل يمكن لدالة واحدة إلقاء أنواع مختلفة من الأخطاء؟

لا يحدد Swift أنواع الأخطاء الملقاة — أي دالة throwing يمكنها إلقاء أي نوع يتوافق مع Error. افصل المعالجة عبر المطابقة في catch حسب أنواع الأخطاء المحددة.

هل يجب أن أغلف جميع الأخطاء في نوع مخصص؟

نعم، هذه ممارسة جيدة. عرف AppError كـ enum يتوافق مع Error وحول أخطاء النظام إلى أخطاء مجال. هذا يوحد المعالجة ويعزل طبقة التطبيق عن تفاصيل النظام.

الخلاصة

  • Do-Catch هو بناء Swift لمعالجة الأخطاء مع وضع علامة throws إلزامية على الدوال و try عند الاستدعاء.
  • أخطاء Swift هي قيم تتوافق مع بروتوكول Error، وليست استثناءات مع فك تكديس المكدس، مما يوفر عبء صفر عند عدم حدوث خطأ.
  • تسمح كتل catch المتعددة بمعالجة أنواع مختلفة من الأخطاء بشكل منفصل مع مطابقة الأنماط المشابهة لـ switch.
  • try? يحول الخطأ إلى nil للسيناريوهات البسيطة، try! هو استدعاء قسري مع خطر التعطل، يستخدم فقط للعمليات المضمونة النجاح.
  • Do-Catch مع async/await هو الطريقة القياسية لمعالجة الأخطاء في كود Swift غير المتزامن باستخدام try await.
  • تجنب كتل catch الفارغة والإفراط في استخدام try! — هذا يخفي الأخطاء ويؤدي إلى أعطال غير متوقعة في الإنتاج.

سنقوم بتطوير تطبيق جوال جاهز

تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.

مناقشة المشروع

اقرأ أيضًا