Section 5/161 menit
5. Sendable Protocol — Deep Dive
5. Sendable Protocol — Deep Dive
Hierarki Sendability
swift
// 1. Struct dengan semua stored properties Sendable → otomatis Sendable
struct User: Sendable {
let id: UUID // Sendable
let name: String // Sendable
let age: Int // Sendable
}
// 2. Enum dengan semua associated values Sendable → otomatis Sendable
enum Result<T: Sendable>: Sendable {
case success(T)
case failure(Error) // Error adalah Sendable (existential)
}
// 3. Class harus explicit — dan butuh internal synchronization
final class SendableCounter: Sendable {
// Harus final agar tidak ada subclass yang break invariant
private let lock = NSLock()
private var _count: Int = 0
var count: Int {
lock.withLock { _count }
}
func increment() {
lock.withLock { _count += 1 }
}
}
// Atau gunakan actor (lebih idiomatis)
actor Counter {
private var count: Int = 0
func increment() { count += 1 }
func value() -> Int { count }
}
@unchecked Sendable — Escape Hatch
swift
// Gunakan @unchecked Sendable hanya jika:
// 1. Kamu yakin synchronization sudah benar secara manual
// 2. Type dari external library yang kamu tahu thread-safe
// 3. Wrapping C/ObjC type yang memiliki documented thread safety
final class ThreadSafeQueue<Element>: @unchecked Sendable {
private var storage: [Element] = []
private let lock = NSLock()
func enqueue(_ element: Element) {
lock.withLock { storage.append(element) }
}
func dequeue() -> Element? {
lock.withLock {
guard !storage.isEmpty else { return nil }
return storage.removeFirst()
}
}
}
// Wrapping DispatchQueue sebagai @unchecked Sendable
// DispatchQueue sendiri Sendable, tapi closure yang di-submit tidak
extension DispatchQueue: @unchecked Sendable { } // sudah built-in
Sendable Closure
swift
// @Sendable closure — dapat dieksekusi di isolation domain yang berbeda
typealias SendableWork = @Sendable () async -> Void
func scheduleWork(_ work: @Sendable @escaping () async -> Void) {
Task.detached(operation: work)
}
// Non-Sendable closure — hanya bisa dieksekusi di domain yang sama
func scheduleLocal(_ work: () async -> Void) {
// ❌ Tidak bisa di-pass ke Task.detached — bukan Sendable
Task { await work() } // ✓ Task inherit isolation, aman
}
// Practical example: background processing
actor ImageProcessor {
func process(
imageData: Data,
completion: @Sendable @escaping (Data) -> Void
) async {
let result = performHeavyProcessing(imageData)
completion(result) // dipanggil dari actor context
}
}
Conditional Sendable
swift
// Generic type yang Sendable hanya jika type parameter-nya Sendable
struct Box<T> {
let value: T
}
// Otomatis: Box<Int> adalah Sendable, Box<NonSendableClass> tidak
extension Box: Sendable where T: Sendable { }
// Verifikasi
let intBox = Box(value: 42)
let stringBox = Box(value: "hello")
// intBox dan stringBox: Sendable ✓
class NotSendable { var x = 0 }
let classBox = Box(value: NotSendable())
// classBox: tidak Sendable — karena NotSendable bukan Sendable