1. Pengantar: Mengapa Isolation?
1. Pengantar: Mengapa Isolation?
1.1 Masalah Fundamental: Memori Bersama
Komputer modern menjalankan banyak hal bersamaan. CPU punya banyak core, dan setiap core bisa mengeksekusi instruksi secara paralel. Ini bagus untuk performa — tapi menimbulkan masalah serius ketika dua core mengakses lokasi memori yang sama secara bersamaan.
Bayangkan dua orang mencoba menulis di papan tulis yang sama pada saat yang sama. Hasilnya bukan gabungan tulisan keduanya — hasilnya adalah coretan yang tidak terbaca.
Di level hardware, ini terjadi karena instruksi seperti value += 1 sebenarnya bukan satu instruksi atomik — ia adalah tiga langkah:
1. LOAD value dari memori → register CPU
2. ADD register + 1
3. STORE hasil kembali ke memori
Jika dua thread melakukan ini bersamaan:
Thread A: LOAD value (dapat: 5)
Thread B: LOAD value (dapat: 5) ← keduanya baca nilai lama!
Thread A: ADD 5 + 1 = 6
Thread B: ADD 5 + 1 = 6
Thread A: STORE 6
Thread B: STORE 6 ← increment B menimpa increment A!
Hasil akhir: value = 6, padahal seharusnya 7. Ini adalah data race — dan ini adalah undefined behavior di Swift (dan C/C++/Rust). Artinya compiler bebas menghasilkan output apa saja, termasuk crash, nilai salah, atau kerusakan memori yang baru muncul jauh kemudian.
1.2 Solusi Tradisional dan Masalahnya
Sebelum Swift 6, developer mengandalkan beberapa pendekatan:
DispatchQueue (Serial Queue)
// "Semua akses ke data ini harus lewat queue ini"
let queue = DispatchQueue(label: "com.app.counter")
var value = 0
queue.async { value += 1 } // thread-safe
queue.async { value += 1 } // thread-safe
Masalah: Tidak ada yang mencegah developer secara tidak sengaja mengakses value langsung tanpa queue. Compiler tidak tahu tentang aturan ini — itu ada di kepala developer saja.
NSLock / os_unfair_lock
let lock = NSLock()
var value = 0
lock.lock()
value += 1
lock.unlock()
Masalah: Mudah lupa unlock (→ deadlock), tidak bisa dikomposisi (lock di dalam lock → deadlock), dan tidak ada pemeriksaan compiler.
Keduanya adalah konvensi, bukan kontrak. Compiler tidak bisa memverifikasi bahwa kamu mengikuti aturan. Satu kesalahan programmer cukup untuk merusak segalanya.
1.3 Filosofi Swift 6: "If It Compiles, It's Safe"
Swift 6 mengambil pendekatan berbeda: encode aturan keamanan ke dalam type system. Jika kodenya compile, maka secara definisi tidak ada data race — karena compiler yang memverifikasinya, bukan manusia.
// Swift 5 — tidak ada compile-time check, ini bisa crash di runtime
class Counter {
var value = 0
func increment() {
value += 1 // DANGER: tidak thread-safe
}
}
let counter = Counter()
Task { counter.increment() }
Task { counter.increment() } // data race! — hanya ketahuan saat runtime crash
// Swift 6 — ini adalah COMPILE ERROR, bukan runtime crash
// error: sending 'counter' risks causing data races
Pergeseran ini fundamental: dari "runtime safety" ke "compile-time safety".
1.4 Isolation = Kepemilikan Data yang Jelas
Isolation adalah kontrak: "data ini hanya boleh diakses dari satu domain pada satu waktu."
Analoginya: bayangkan sebuah kantor dengan ruangan-ruangan terpisah. Dokumen yang ada di ruangan A hanya bisa diakses oleh orang yang berada di ruangan A. Untuk mengakses dokumen dari ruangan B, kamu harus mengirim pesan ke ruangan A dan menunggu balasan — kamu tidak bisa langsung masuk.
Domain isolasi di Swift:
actorinstance — setiap actor adalah "ruangan" sendiri@MainActor— ruangan khusus: main thread- Custom global actor — ruangan bersama yang diberi nama
- Nonisolated — di luar ruangan mana pun, hanya bisa akses data yang aman dibagi
| Swift 5 | Swift 6 | |
|---|---|---|
| Data race check | Runtime (crash tidak terduga) | Compile-time (error jelas) |
| Concurrency warning | Opt-in | Error by default |
| Isolation enforcement | Manual (konvensi) | Type system (compiler) |
| Keterbacaan aturan | Di komentar / dokumen | Di kode itu sendiri |
1.5 Kapan Isolation Diperlukan dan Kapan Tidak
Isolation diperlukan ketika:
- Kamu punya mutable state yang bisa diakses dari beberapa concurrent contexts
- Kamu menggunakan async/await dan melewatkan data antar tasks
- Kamu menggunakan background processing + UI update
Isolation tidak diperlukan ketika:
- Semua state adalah immutable (
let) — tidak bisa di-mutate, tidak ada race - Kamu menggunakan value types yang di-copy saat diteruskan — setiap Task punya salinannya sendiri
- Kamu sudah menggunakan mekanisme lain yang terbukti aman (misalnya atomic operations untuk tipe primitif sederhana)
// TIDAK perlu isolation: value type + immutable
let config = AppConfig(timeout: 30, retries: 3) // struct, let
Task { process(config) } // Task punya salinannya sendiri — aman
Task { process(config) } // aman juga
// PERLU isolation: reference type + mutable
class AppState {
var currentUser: User? // shared reference, bisa di-mutate
}