Sendable — ما هو، بروتوكول أمان الخيوط

المؤلف: IT Sectr نُشر: 2026-06-18 وقت القراءة: 10 دق

Sendable هو بروتوكول Swift يحدد الأنواع الآمنة للنقل بين الخيوط. عند العمل مع التزامن (actor, async/await, Task)، يتطلب مترجم Swift أن جميع البيانات المنقولة بين السياقات المعزولة تتوافق مع Sendable. هذا يمنع النقل العرضي للأنواع غير الآمنة التي قد تؤدي إلى سباقات البيانات. يعمل البروتوكول كعقد: النوع المتوافق مع Sendable يضمن عدم وجود حالة داخلية غير متزامنة. وفقًا لـ WWDC 2021، فإن بروتوكول Sendable عنصر إلزامي عند تصميم بنية آمنة متعددة الخيوط.

الخلاصة

  • Sendable — بروتوكول يضمن عدم وجود سباقات بيانات عند النقل بين الخيوط
  • أنواع القيمة (struct, enum) تتوافق تلقائيًا مع Sendable إذا كانت جميع خصائصها Sendable
  • @unchecked Sendable — آلية تجاوز للفئات التي يتحمل المطور مسؤولية أمانها
  • إغلاقات Sendable تُوسم بالسمة @Sendable التي تتحقق من التقاط المتغيرات
  • المترجم يُصدر خطأ إذا تم نقل نوع غير Sendable بين سياقات معزولة

ما هو بروتوكول Sendable في Swift؟

Sendable هو بروتوكول من المكتبة القياسية لـ Swift (SE-0302) يحدد الأنواع الآمنة للنقل بين نطاقات العزل. نطاق العزل يعني actor أو مهمة Task أو سياق @MainActor. لا يحتوي Sendable على متطلبات — إنه يعمل كعلامة أمان.

لماذا نحتاج Sendable

قبل Swift 5.5، كان بإمكان المطورين نقل أي كائن بين الخيوط عبر DispatchQueue، ولم يتحقق المترجم مما إذا كان آمنًا. Sendable يسد هذه الثغرة: الآن يتتبع المترجم نفسه عمليات النقل عبر الخيوط ويمنع غير الآمنة. هذا يجعل الكود المتزامن أكثر موثوقية حتى قبل تشغيله.

swift
struct UserProfile: Sendable {
    let name: String
    let age: Int
}

class NonSendableClass {
    var counter: Int = 0
}

في المثال، يمكن نقل UserProfile بأمان بين الخيوط لأنه هيكل بخصائص ثابتة من أنواع Sendable. سيتسبب NonSendableClass في خطأ ترجمة عند محاولة نقله عبر await أو إلى Task.

ما هي الأنواع المتوافقة مع Sendable افتراضيًا

أنواع Sendable في Swift تنقسم إلى ثلاث فئات: أنواع القيمة بخصائص Sendable، والفئات النهائية بحالة غير قابلة للتغيير، والدوال/الإغلاقات الموسومة بـ @Sendable. يستنتج المترجم تلقائيًا توافق Sendable لأنواع القيمة.

أنواع القيمة والتوافق التلقائي

Struct, enum وtuple تصبح تلقائيًا Sendable إذا كانت جميع خصائصها وقيمها المرتبطة أيضًا Sendable. هذا نهج تحفظي: إذا كانت خاصية واحدة على الأقل غير متوافقة مع Sendable، فلن يتم التعرف على النوع بالكامل كآمن.

النوعSendableالشرط
Structنعم (بدون تصريح صريح)جميع الخصائص Sendable
Enumنعم (بدون تصريح صريح)جميع القيم المرتبطة Sendable
Final classنعم (مع تصريح صريح)جميع خصائص let هي Sendable، لا يوجد var
Non-final classلالا يمكن أن تكون Sendable بسبب الوراثة

الأنواع الأساسية

جميع الأنواع المضمنة في Swift — Int, String, Double, Bool, Optional, Array, Dictionary, Set — تتوافق مع Sendable. هذا يجعل تركيب الأنواع آمنًا افتراضيًا. يحتاج المطور فقط إلى مراقبة الفئات المخصصة.

Sendable والفئات: @unchecked Sendable

@unchecked Sendable هي آلية تسمح للفئة بتصريح نفسها صراحةً على أنها Sendable، متجاوزة فحوصات المترجم. يتحمل المطور مسؤولية أمان الخيوط لهذه الفئة. هذا مفيد لجسور Objective-C والهياكل المحسّنة.

متى تستخدم @unchecked Sendable

يُستخدم @unchecked Sendable عندما تضمن الفئة داخليًا الأمان من خلال الأقفال أو العمليات الذرية، لكن المترجم لا يمكنه التحقق من ذلك بشكل ثابت. على سبيل المثال، فئة بها os_unfair_lock أو pthread_mutex_t — أمان خيوطها مضمون بالكود، لكن Swift لا يرى ذلك.

swift
final class AtomicCounter: @unchecked Sendable {
    private var value: Int = 0
    private let lock = NSLock()
    
    func increment() {
        lock.lock()
        value += 1
        lock.unlock()
    }
}

يجب استخدام @unchecked Sendable بحذر. إنها إشارة صريحة للمطورين الآخرين: «لقد تحققت، هذا آمن». خطأ في التنفيذ يمكن أن يؤدي إلى سباقات يصعب اكتشافها. قبل استخدام @unchecked، تأكد من أن النوع لا يمكن إعادة كتابته كنوع قيمة.

Sendable مع Actor

Actor و Sendable وجهان لعملة واحدة. Actor يعزل حالته، ولكن لتبادل البيانات مع العالم الخارجي يجب أن يعيد أنواع Sendable. إذا أعادت دالة actor نوعًا غير Sendable، يصدر المترجم تحذيرًا أو خطأ.

نقل البيانات عبر Sendable

عندما يرسل actor بيانات إلى كود خارجي، تعبر هذه البيانات حدود العزل. Sendable يضمن أن المستلم يمكنه استخدامها بأمان خارج actor. يبقى actor نفسه معزولًا — حالته الداخلية لا يتم كشفها.

swift
struct AccountSnapshot: Sendable {
    let id: UUID
    let balance: Double
    let lastUpdated: Date
}

actor BankActor {
    private var balance: Double = 0
    
    func snapshot() async -> AccountSnapshot {
        return AccountSnapshot(
            id: UUID(),
            balance: balance,
            lastUpdated: Date()
        )
    }
}

AccountSnapshot هو هيكل Sendable يحتوي فقط على خصائص let من أنواع Sendable. هذا النهج هو أفضل ممارسة لاستخراج البيانات من actor. لقطة الحالة تُنقل بالقيمة، ولا يفقد actor السيطرة على حالته.

دوال وإغلاقات Sendable

@Sendable هي سمة للدوال والإغلاقات تضمن أن الإغلاق لا يلتقط بيانات غير Sendable بطريقة قابلة للتغيير. عندما يُنقل إغلاق إلى Task أو دالة actor، يجب أن يكون Sendable.

فحص الالتقاط في إغلاقات @Sendable

يتحقق المترجم من أن إغلاق @Sendable لا يلتقط مراجع قابلة للتغيير للفئات. يُسمح بالتقاط خصائص let من أنواع Sendable. التقاط متغير var من نوع مرجعي سيؤدي إلى خطأ لأنه قد يتم تنفيذ الإغلاق بالتزامن مع التغيير.

swift
func performAsync(operation: @Sendable () -> Void) {
    Task {
        await operation()
    }
}

let constant = "Safe"
var counter = 0
// counter — التقاط var يغير counter، خطأ في الترجمة
performAsync { // ❌ تغيير متغير ملتقط
    print(constant)
}

القاعدة بسيطة: إغلاق @Sendable يمكنه فقط التقاط البيانات التي هي نفسها Sendable ولن يتم تغييرها من الخارج. للفئات، يُسمح بالتقاط مرجع ضعيف weak self إذا لم تكن الفئة موسومة كـ Sendable. هذا يمنع دورات الاحتفاظ الكلاسيكية وسباقات البيانات.

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

ما هو Sendable في Swift بكلمات بسيطة؟

Sendable هو علامة تخبر المترجم: «يمكن نقل هذا النوع بأمان بين الخيوط». الهياكل والثوابت عادة ما تكون Sendable افتراضيًا، الفئات لا تكون كذلك إلا إذا تم تحديد ذلك صراحةً.

ما هي الأنواع المتوافقة تلقائيًا مع Sendable؟

أنواع القيمة (struct, enum) بخصائص Sendable، والفئات النهائية بحالة غير قابلة للتغيير، وجميع الأنواع الأساسية في Swift: Int, String, Double, Bool, Array, Dictionary, Optional — تتوافق مع Sendable تلقائيًا.

ما هو @unchecked Sendable؟

@unchecked Sendable هي طريقة لتصريح فئة كـ Sendable بدون فحوصات ثابتة من المترجم. يضمن المطور الأمان بنفسه، غالبًا من خلال الأقفال أو العمليات الذرية.

لماذا نحتاج Sendable لـ Actor؟

Actor يعزل حالته، ولكن عند إعادة البيانات إلى الكود الخارجي، تعبر هذه البيانات حدود العزل. Sendable يضمن أن المستلم لن يواجه سباقات عند استخدام هذه البيانات خارج actor.

كيف أجعل فئة Sendable في Swift؟

أضف بروتوكول Sendable إلى فئة نهائية، جميع خصائصها ثوابت من أنواع Sendable. إذا كانت الفئة تستخدم أقفالًا، يمكنك استخدام @unchecked Sendable، لكن هذا يتطلب الحذر.

الملخص

  • Sendable — بروتوكول علامة للنقل الآمن للبيانات عبر الخيوط
  • أنواع القيمة تتوافق تلقائيًا مع Sendable عند استيفاء الشروط
  • الفئات تتطلب تصريحًا صريحًا بـ Sendable أو @unchecked Sendable
  • Actor يعيد البيانات إلى الكود الخارجي فقط من خلال أنواع Sendable
  • @Sendable سمة الإغلاق تتحقق من صحة التقاط المتغيرات
  • المترجم يتحقق ثابتًا من توافق Sendable، ويمنع سباقات البيانات في وقت البناء

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

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

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

اقرأ أيضًا