5. Region-Based Isolation (Swift 6 Baru)
5. Region-Based Isolation (Swift 6 Baru)
5.1 Masalah yang Dipecahkan
Sebelum Swift 6, aturannya binary: sebuah nilai harus Sendable untuk dikirim ke isolation domain lain. Ini terlalu ketat dalam banyak kasus.
Pertimbangkan:
func buildRequest() -> URLRequest {
var request = URLRequest(url: someURL)
request.httpMethod = "POST"
request.httpBody = someData
return request
}
// URLRequest bukan Sendable (di Swift 5.x)
// Tapi kita tahu bahwa setelah kita "kirim" ke network task,
// kita tidak akan mengakses request itu lagi!
// Swift 5.x: ERROR — URLRequest tidak Sendable
Task { try await URLSession.shared.data(for: buildRequest()) }
Aturan Sendable terlalu keras di sini karena kita tahu secara statik bahwa tidak ada concurrent access — kita tidak menyimpan referensi lain ke request itu.
5.2 Teori: Isolation Regions
Swift 6 memperkenalkan analisis region-based isolation — compiler melacak "region" atau "kelompok kepemilikan" setiap nilai, bukan hanya tipenya.
Sebuah isolation region adalah sekumpulan nilai yang saling terhubung melalui referensi. Jika dua nilai bisa saling menjangkau satu sama lain (misalnya object graph), mereka berada dalam region yang sama.
class Node {
var next: Node?
var value: Int
}
var head = Node(value: 1) // head
head.next = Node(value: 2) // head.next
head.next?.next = Node(value: 3) // head.next.next
// Ketiga node ini berada dalam SATU region
// karena mereka terhubung melalui referensi 'next'
Ide kunci: Jika sebuah region tidak lagi dapat diakses dari konteks asal setelah "dikirim", maka aman untuk dikirim ke isolation domain lain — meskipun nilainya tidak Sendable.
5.3 sending Keyword
sending parameter menandai bahwa nilai ditransfer — kepemilikannya berpindah ke callee. Setelah transfer, caller tidak boleh mengakses nilai tersebut lagi:
struct LargeModel { // bukan Sendable
var items: [String] = []
var metadata: [String: Any] = [:]
}
// 'sending' = "saya mengambil alih kepemilikan model ini"
func processInBackground(_ model: sending LargeModel) async {
// model sekarang milik fungsi ini
// caller tidak bisa akses model setelah memanggil ini
var processed = model
processed.items.sort()
await persist(processed)
}
// Penggunaan:
var model = LargeModel()
model.items = ["c", "a", "b"]
await processInBackground(model) // transfer terjadi di sini
// ❌ ERROR: compiler tahu 'model' sudah di-transfer
// print(model.items) // 'model' digunakan setelah dikirim ke isolation lain
Analogi: Ini seperti move semantics di C++ atau move di Rust. Nilai "dipindahkan", bukan "disalin" atau "dibagi".
Kapan gunakan sending:
- Fungsi yang mengkonsumsi nilai dan tidak perlu dikembalikan
- Pipeline processing: data masuk, diproses, tidak perlu kembali
- Ketika kamu ingin izinkan tipe non-Sendable melewati isolation boundary secara satu arah
Kapan TIDAK gunakan sending:
- Ketika caller masih butuh akses ke nilai setelah call
- Ketika nilai perlu dibaca dari beberapa tempat — gunakan Sendable struct sebagai gantinya
5.4 Mengapa Ini Penting di Praktik
// Swift 5.9 → Swift 6: Lebih banyak kode yang compile tanpa perubahan
// Sebelumnya butuh workaround
func sendRequest(_ request: URLRequest) async throws -> Data {
let (data, _) = try await URLSession.shared.data(for: request)
return data
}
// Swift 6: 'sending' memungkinkan ini secara eksplisit
func sendRequest(_ request: sending URLRequest) async throws -> Data {
let (data, _) = try await URLSession.shared.data(for: request)
return data
}
Region-based isolation adalah tentang memberi compiler informasi yang lebih kaya tentang kapan nilai bisa diakses, bukan hanya bagaimana nilai itu direpresentasikan di memori.