Section 2/161 menit
2. Masalah yang Dipecahkan di Level Lanjutan
2. Masalah yang Dipecahkan di Level Lanjutan
Masalah 1: Sendable Check Terlalu Ketat untuk Nilai yang Jelas Aman
Sebelum region-based isolation, Swift 6 melarang transfer nilai non-Sendable meskipun compiler bisa membuktikan tidak ada akses bersamaan.
swift
// ❌ Swift 6 tanpa region-based isolation: error meski secara logis aman
class Document { // Non-Sendable — class tanpa Sendable conformance
var content: String = ""
}
func processDocument() async {
let doc = Document()
doc.content = "Draft"
await Task.detached {
// ❌ Error: 'doc' tidak Sendable, tidak bisa dikirim ke Task baru
print(doc.content)
}.value
// Padahal setelah Task.detached, 'doc' tidak diakses lagi di sini
}
swift
// ✓ DENGAN region-based isolation: compiler tahu 'doc' sudah "dikirim" dan tidak diakses lagi
func processDocument() async {
let doc = Document()
doc.content = "Draft"
// Compiler analisis: setelah baris ini, 'doc' tidak digunakan lagi di region asal
await Task.detached { [doc] in // Transfer ownership — region analysis membuktikan aman
print(doc.content)
}.value
}
Masalah 2: Actor Re-entrancy Menyebabkan State Inconsistency
Programmer baru sering berasumsi actor berjalan atomically dari awal sampai akhir method. Ini salah.
swift
// ❌ TANPA pemahaman re-entrancy: asumsi state konsisten di seluruh await
actor BankAccount {
var balance: Double = 1000
func transfer(amount: Double, to other: BankAccount) async throws {
guard balance >= amount else { throw TransferError.insufficient }
// MASALAH: di antara guard dan pengurangan, ada await
// Method lain bisa berjalan di actor ini dan mengubah balance!
await other.deposit(amount: amount) // Actor suspension point
balance -= amount // Balance bisa sudah berbeda dari saat guard!
}
}
swift
// ✓ DENGAN pemahaman re-entrancy: simpan snapshot atau lakukan operasi atomically
actor BankAccount {
var balance: Double = 1000
func transfer(amount: Double, to other: BankAccount) async throws {
// Baca dan ubah balance SEBELUM suspension point
guard balance >= amount else { throw TransferError.insufficient }
balance -= amount // Lakukan sekarang, sebelum await
// Sekarang baru await — jika gagal, rollback
do {
await other.deposit(amount: amount)
} catch {
balance += amount // Rollback
throw error
}
}
}
Masalah 3: Closure yang Capture Isolation Secara Tidak Terduga
Closure tidak selalu mewarisi isolation context tempat ia didefinisikan — tergantung konteksnya.
swift
// ❌ Membingungkan: kapan closure ikut @MainActor, kapan tidak?
@MainActor
class ViewController: UIViewController {
var data: [String] = []
func loadData() {
// Closure ini @MainActor? Atau tidak?
Task {
let result = await fetchFromNetwork()
self.data = result // Aman atau data race?
}
}
}
swift
// ✓ Dipahami: Task closure mewarisi isolation dari enclosing context
@MainActor
class ViewController: UIViewController {
var data: [String] = []
func loadData() {
// Task {} mewarisi @MainActor dari ViewController
// Semua kode di dalamnya dijalankan di MainActor
Task {
let result = await fetchFromNetwork() // suspend → kembali ke @MainActor
self.data = result // ✓ Aman — dijalankan di @MainActor
}
// Task.detached {} TIDAK mewarisi isolation
Task.detached {
// Ini BUKAN di @MainActor
// self.data = ... // ❌ Error: harus await MainActor.run
}
}
}