Sendable — adalah protokol Swift yang menandai tipe yang aman untuk dikirim antar thread. Saat bekerja dengan konkurensi (actor, async/await, Task), kompiler Swift memerlukan semua data yang dikirim antar konteks terisolasi untuk sesuai dengan Sendable. Ini mengecualikan pengiriman tidak sengaja tipe tidak aman yang dapat menyebabkan race condition. Protokol bertindak sebagai kontrak: tipe yang sesuai dengan Sendable menjamin tidak adanya keadaan internal yang tidak disinkronkan. Menurut WWDC 2021, protokol Sendable adalah elemen wajib dalam merancang arsitektur multi-thread yang aman.
Utama
Sendable — adalah protokol dari perpustakaan standar Swift (SE-0302) yang menandai tipe yang aman untuk dikirim antar domain isolasi. Domain isolasi mencakup actor, tugas Task, atau konteks @MainActor. Sendable tidak berisi persyaratan — ia bertindak sebagai penanda keamanan.
Sebelum Swift 5.5, pengembang dapat mengirim objek apa pun antar thread melalui DispatchQueue, dan kompiler tidak memeriksa apakah itu aman. Sendable menutup celah ini: sekarang kompiler sendiri melacak pengiriman antar thread dan memblokir yang tidak aman. Ini membuat kode konkuren lebih andal bahkan sebelum dijalankan.
struct UserProfile: Sendable {
let name: String
let age: Int
}
class NonSendableClass {
var counter: Int = 0
}
Dalam contoh, UserProfile dapat dikirim dengan aman antar thread, karena ini adalah struktur dengan properti konstan dari tipe Sendable. NonSendableClass akan menyebabkan kesalahan kompilasi saat mencoba mengirim melalui await atau ke Task.
Tipe Sendable di Swift dibagi menjadi tiga kategori: tipe nilai dengan properti Sendable, kelas final dengan keadaan tidak dapat diubah, dan fungsi/penutupan yang ditandai dengan @Sendable. Kompiler secara otomatis menyimpulkan kesesuaian Sendable untuk tipe nilai.
Struct, enum dan tuple secara otomatis menjadi Sendable jika semua properti dan nilai terkaitnya juga Sendable. Ini adalah pendekatan konservatif: jika bahkan satu properti tidak sesuai dengan Sendable, seluruh tipe tidak akan dianggap aman.
| Tipe | Sendable | Syarat |
|---|---|---|
| Struct | Ya (tanpa indikasi eksplisit) | Semua properti Sendable |
| Enum | Ya (tanpa indikasi eksplisit) | Semua nilai terkait Sendable |
| Final class | Ya (dengan indikasi eksplisit) | Semua properti let Sendable, tidak ada var |
| Non-final class | Tidak | Tidak bisa menjadi Sendable karena pewarisan |
Semua tipe bawaan Swift — Int, String, Double, Bool, Optional, Array, Dictionary, Set — sesuai dengan Sendable. Ini membuat komposisi tipe aman secara default. Pengembang hanya perlu memperhatikan kelas khusus.
@unchecked Sendable — adalah mekanisme yang memungkinkan kelas untuk secara eksplisit mendeklarasikan dirinya sebagai Sendable, melewati pemeriksaan kompiler. Pengembang mengambil tanggung jawab untuk keamanan thread dari kelas tersebut. Ini berguna untuk jembatan Objective-C dan struktur yang dioptimalkan.
@unchecked Sendable diterapkan ketika kelas secara internal menjamin keamanan melalui kunci atau operasi atomik, tetapi kompiler tidak dapat memeriksanya secara statis. Misalnya, kelas dengan os_unfair_lock atau pthread_mutex_t — keamanan thread-nya dijamin oleh kode, tetapi Swift tidak melihatnya.
final class AtomicCounter: @unchecked Sendable {
private var value: Int = 0
private let lock = NSLock()
func increment() {
lock.lock()
value += 1
lock.unlock()
}
}
Gunakan @unchecked Sendable dengan hati-hati. Ini adalah sinyal yang jelas bagi pengembang lain: “Saya sudah memeriksa, ini aman”. Kesalahan dalam implementasi dapat menyebabkan race condition yang sulit dilacak. Sebelum menerapkan @unchecked, pastikan tipe tersebut benar-benar tidak dapat ditulis ulang sebagai tipe nilai.
Actor dan Sendable adalah dua sisi dari koin yang sama. Actor mengisolasi keadaannya, tetapi untuk pertukaran data dengan dunia luar ia harus mengembalikan tipe Sendable. Jika metode actor mengembalikan tipe non-Sendable, kompiler mengeluarkan peringatan atau kesalahan.
Ketika actor mengirim data ke kode eksternal, data ini melintasi batas isolasi. Sendable menjamin bahwa penerima dapat menggunakannya dengan aman di luar actor. Actor itu sendiri tetap terisolasi — keadaan internalnya tidak diungkapkan.
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 — adalah struktur Sendable yang hanya berisi properti let dari tipe Sendable. Pendekatan ini adalah praktik terbaik untuk mengekstrak data dari actor. Snapshot keadaan dikirim berdasarkan nilai, dan actor tidak kehilangan kendali atas keadaannya.
@Sendable — adalah atribut untuk fungsi dan penutupan yang menjamin bahwa penutupan tidak menangkap data non-Sendable dengan cara yang dapat diubah. Ketika penutupan dikirim ke Task atau metode actor, ia harus Sendable.
Kompiler memeriksa bahwa penutupan @Sendable tidak menangkap referensi yang dapat diubah ke kelas. Penangkapan properti let dari tipe Sendable diizinkan. Penangkapan variabel var dari tipe referensi akan menyebabkan kesalahan, karena penutupan dapat dijalankan bersamaan dengan mutasi.
func performAsync(operation: @Sendable () -> Void) {
Task {
await operation()
}
}
let constant = "Safe"
var counter = 0
// counter — menangkap var memutasi penghitung, kesalahan kompilasi
performAsync { // ❌ Mutasi var yang ditangkap
print(constant)
}
Aturannya sederhana: penutupan @Sendable hanya dapat menangkap data yang Sendable dan tidak akan dimutasi dari luar. Untuk kelas, penangkapan referensi lemah weak self diizinkan jika kelas tidak ditandai sebagai Sendable. Ini mencegah retain cycle klasik dan race condition.
Pertanyaan yang Sering Diajukan
Sendable — adalah penanda yang memberi tahu kompiler: “Tipe ini dapat dikirim dengan aman antar thread”. Struktur dan konstanta biasanya Sendable secara default, kelas — tidak, kecuali dinyatakan secara eksplisit.
Tipe nilai (struct, enum) dengan properti Sendable, kelas final dengan keadaan tidak dapat diubah, serta semua tipe dasar Swift: Int, String, Double, Bool, Array, Dictionary, Optional — secara otomatis sesuai dengan Sendable.
@unchecked Sendable — adalah cara untuk mendeklarasikan kelas sebagai Sendable tanpa pemeriksaan statis kompiler. Pengembang menjamin keamanan secara mandiri, sering melalui kunci atau operasi atomik.
Actor mengisolasi keadaannya, tetapi saat mengembalikan data ke kode eksternal, data ini melintasi batas isolasi. Sendable menjamin bahwa penerima tidak akan menghadapi race condition saat menggunakan data ini di luar actor.
Tambahkan protokol Sendable ke kelas final yang semua propertinya adalah konstanta dari tipe Sendable. Jika kelas menggunakan kunci, @unchecked Sendable dapat diterapkan, tetapi ini memerlukan kehati-hatian.
Kesimpulan
Kami akan mengembangkan aplikasi seluler turnkey
IT Sectr membuat aplikasi iOS dan Android untuk startup dan bisnis sejak 2017. Kami akan memberi saran dan mengusulkan solusi terbaik.
Baca juga