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
        }
    }
}