Sendable — این یک پروتکل Swift است که تیپهایی را که برای انتقال بین رشتهها ایمن هستند، علامتگذاری میکند. هنگام کار با همزمانی (actor, async/await, Task)، کامپایلر Swift نیاز دارد که همه دادههایی که بین کنتکستهای جداگانه انتقال مییابند، با Sendable مطابقت داشته باشند. این انتقال تصادفی تیپهای ناامنی را که میتوانند به مسابقه دادهها منجر شوند، مرتفع میکند. پروتکل به عنوان یک قرارداد عمل میکند: تیپی که با Sendable مطابقت دارد، ضمانت میدهد که وضعیت داخلی همگامسازینشدهای ندارد. به گزارش WWDC 2021، پروتکل Sendable یک عنصر اجباری در طراحی معماری ایمن چندرشتهای است.
نکات کلیدی
Sendable — این یک پروتکل از کتابخانه استاندارد Swift (SE-0302) است که تیپهای ایمن برای انتقال بین دامنههای جداگانه را علامتگذاری میکند. دامنه جداگانه شامل actor، وظیفه Task یا کنتکست @MainActor است. Sendable هیچ نیازمندی ندارد — آن به عنوان یک نشانگر ایمنی عمل میکند.
قبل از Swift 5.5، توسعهدهندگان میتوانستند هر شیئی را از طریق DispatchQueue بین رشتهها انتقال دهند و کامپایلر بررسی نمیکرد که آیا این ایمن است یا خیر. Sendable این شکاف را میبندد: اکنون کامپایلر خود انتقالات بینرشتهای را ردیابی میکند و انتقالات ناامن را مسدود میکند. این کد همزمان را حتی قبل از اجرا بیشتر قابل اعتماد میکند.
struct UserProfile: Sendable {
let name: String
let age: Int
}
class NonSendableClass {
var counter: Int = 0
}
در مثال، UserProfile میتواند بهطور ایمن بین رشتهها انتقال یابد، زیرا این یک ساختار با ویژگیهای ثابت از انواع Sendable است. NonSendableClass در صورت انتقال از طریق await یا Task باعث خطای کامپایل خواهد شد.
انواع Sendable در Swift به سه دسته تقسیم میشوند: انواع مقداری با ویژگیهای Sendable، کلاسهای final با وضعیت تغییرناپذیر و توابع/بستههایی که با @Sendable علامتگذاری شدهاند. کامپایلر بهطور خودکار مطابقت Sendable را برای انواع مقداری استنباط میکند.
Struct, enum و tuple اگر تمامی ویژگیها و مقادیر همراه آنها نیز Sendable باشند، بهطور خودکار Sendable میشوند. این یک رویکرد محافظهکارانه است: اگر حتی یک ویژگی با Sendable مطابقت نداشته باشد، کل نوع ایمن تلقی نخواهد شد.
| نوع | Sendable | شرط |
|---|---|---|
| Struct | بله (بدون ذکر صریح) | تمامی ویژگیها Sendable |
| Enum | بله (بدون ذکر صریح) | تمامی associated values Sendable |
| Final class | بله (با ذکر صریح) | تمامی ویژگیهای let Sendable، بدون var |
| Non-final class | خیر | به دلیل وراثت نمیتواند Sendable باشد |
تمامی انواع ساخته شده Swift — Int, String, Double, Bool, Optional, Array, Dictionary, Set — با Sendable مطابقت دارند. این ترکیب انواع را بهطور پیشفرض ایمن میکند. توسعهدهنده فقط باید مراقب کلاسهای سفارشی باشد.
@unchecked Sendable — این مکانیسمی است که به یک کلاس اجازه میدهد خود را صریحاً Sendable اعلام کند و بررسیهای کامپایلر را دور بزند. توسعهدهنده مسئولیت ایمنی رشتهای چنین کلاسی را بر عهده میگیرد. این برای پلهای Objective-C و ساختارهای بهینهشده مفید است.
@unchecked Sendable زمانی استفاده میشود که کلاس بهطور داخلی از طریق قفلها یا عملیات اتمی ایمنی را تضمین میکند، اما کامپایلر نمیتواند این را بهطور استاتیک بررسی کند. به عنوان مثال، کلاسی با os_unfair_lock یا pthread_mutex_t — ایمنی رشتهای آن توسط کد تأمین میشود، اما 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، مطمئن شوید که نوع واقعاً نمیتواند به عنوان یک نوع مقداری بازنویسی شود.
Actor و Sendable دو طرف یک سکه هستند. Actor وضعیت خود را جدا میکند، اما برای تبادل داده با دنیای خارجی باید انواع Sendable را برگرداند. اگر روش actor یک نوع غیر Sendable برگرداند، کامپایلر هشدار یا خطا صادر میکند.
وقتی actor دادههایی را به کد خارجی ارسال میکند، این دادهها از مرز جداگانگی عبور میکنند. Sendable ضمانت میدهد که دریافتکننده میتواند آنها را در خارج از actor بهطور ایمن استفاده کند. خود actor جداگانه باقی میماند — وضعیت داخلی آن فاش نمیشود.
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 را دارد. این رویکرد best practice برای استخراج دادهها از actor است. عکس وضعیت بهصورت مقدار انتقال مییابد و actor کنترل خود را بر وضعیتش از دست نمیدهد.
@Sendable — این یک ویژگی برای توابع و بستهها است که ضمانت میدهد بسته دادههای غیر Sendable را به صورت تغییرپذیر جذب نمیکند. وقتی بسته به Task یا روش actor انتقال مییابد، باید Sendable باشد.
کامپایلر بررسی میکند که @Sendable بسته ارجاعات قابل تغییر به کلاسها را جذب نمیکند. جذب ویژگیهای let از انواع Sendable مجاز است. جذب متغیر var از نوع ارجاعی منجر به خطا میشود، زیرا بسته ممکن است همزمان با تغییر اجرا شود.
func performAsync(operation: @Sendable () -> Void) {
Task {
await operation()
}
}
let constant = "Safe"
var counter = 0
// counter — جذب var کانتر را تغییر میدهد، خطای کامپایل
performAsync { // ❌ تغییر var جذبشده
print(constant)
}
قانون ساده است: @Sendable بسته فقط میتواند دادههایی را جذب کند که خود Sendable هستند و از خارج موتاسیون نخواهند شد. برای کلاسها، اگر کلاس به عنوان Sendable علامتگذاری نشده است، جذب ارجاع weak self مجاز است. این از چرخههای کلاسیک retain و race condition جلوگیری میکند.
سوالات متداول
Sendable — این یک نشانگر است که به کامپایلر میگوید: «این نوع را میتوان بهطور ایمن بین رشتهها انتقال داد». ساختارها و ثابتها معمولاً بهطور پیشفرض Sendable هستند، کلاسها خیر، مگر اینکه صریحاً مشخص شده باشد.
انواع مقداری (struct, enum) با ویژگیهای Sendable، کلاسهای final با وضعیت تغییرناپذیر، و همچنین تمامی انواع پایه Swift: Int, String, Double, Bool, Array, Dictionary, Optional — بهطور خودکار با Sendable مطابقت دارند.
@unchecked Sendable — این راهی است برای اعلام کلاس به عنوان Sendable بدون بررسی استاتیک کامپایلر. توسعهدهنده ایمنی را خودش تضمین میکند، اغلب از طریق قفلها یا عملیات اتمی.
Actor وضعیت خود را جدا میکند، اما وقتی دادهها را به کد خارجی برمیگرداند، این دادهها از مرز جداگانگی عبور میکنند. Sendable ضمانت میدهد که دریافتکننده در استفاده از این دادهها در خارج از actor با مسابقه مواجه نخواهد شد.
پروتکل Sendable را به یک کلاس final که تمامی ویژگیهایش ثابتهایی از انواع Sendable هستند، اضافه کنید. اگر کلاس از قفلها استفاده میکند، میتوان @unchecked Sendable را اعمال کرد، اما این نیازمند احتیاط است.
نتیجه
ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد
IT Sectr از سال 2017 برنامههای iOS و Android را برای استارتاپها و کسبوکارها ایجاد میکند. ما به شما مشاوره میدهیم و بهترین راهحل را پیشنهاد خواهیم کرد.
همچنین بخوانید