Section 2/161 menit

2. Masalah yang Dipecahkan

2. Masalah yang Dipecahkan

Masalah 1: Akses Bersamaan ke Shared State

swift
// ❌ TANPA data race safety: crash atau corruption di runtime
class UserCache {
    var cache: [String: User] = [:]  // mutable shared state
    
    func store(_ user: User, forKey key: String) {
        cache[key] = user  // Thread A bisa baca saat Thread B write
    }
    
    func user(forKey key: String) -> User? {
        return cache[key]  // undefined behavior jika concurrent
    }
}

// Dua thread memanggil ini bersamaan → crash
let userCache = UserCache()
DispatchQueue.global().async { userCache.store(user1, forKey: "a") }
DispatchQueue.global().async { userCache.store(user2, forKey: "b") }
swift
// ✓ DENGAN actor: compiler melarang akses bersamaan
actor UserCache {
    private var cache: [String: User] = [:]
    
    func store(_ user: User, forKey key: String) {
        cache[key] = user  // hanya satu akses pada satu waktu
    }
    
    func user(forKey key: String) -> User? {
        return cache[key]
    }
}

// Compiler memaksa penggunaan await
let userCache = UserCache()
await userCache.store(user1, forKey: "a")
await userCache.store(user2, forKey: "b")  // serialized, tidak ada race

Masalah 2: Closure yang Capture Mutable State

swift
// ❌ TANPA data race safety: closure escape membawa mutable reference
class ViewModel {
    var items: [Item] = []
    
    func loadItems() {
        URLSession.shared.dataTask(with: url) { [self] data, _, _ in
            // ❌ self adalah class — bisa diakses dari thread lain bersamaan
            self.items = parseItems(from: data!)  // data race!
        }.resume()
    }
}
swift
// ✓ DENGAN @MainActor + Sendable: compiler enforce sinkronisasi
@MainActor
class ViewModel: ObservableObject {
    @Published var items: [Item] = []
    
    func loadItems() {
        Task {
            // Task secara explicit inherit isolation @MainActor
            let data = try await URLSession.shared.data(from: url).0
            // Parsing di background (nonisolated context)
            let parsed = await Task.detached { parseItems(from: data) }.value
            // Update UI di main actor
            self.items = parsed  // safe: hanya @MainActor yang bisa akses
        }
    }
}

Masalah 3: Non-Sendable Type Lolos ke Async Context

swift
// ❌ TANPA Sendable enforcement: UIKit objects dikirim ke background thread
func processImage() {
    let image = UIImage(named: "photo")!  // UIImage: non-Sendable, main-thread only
    
    Task.detached {
        // Swift 6: ERROR — UIImage bukan Sendable, tidak boleh crossing isolation boundary
        let processed = applyFilter(to: image)
    }
}
swift
// ✓ DENGAN Sendable design: hanya kirim data yang aman
func processImage() {
    let image = UIImage(named: "photo")!
    
    // Convert ke Sendable representation sebelum crossing boundary
    guard let imageData = image.pngData() else { return }
    
    Task.detached {
        // Data adalah Sendable — aman untuk di-pass ke task terpisah
        let processedData = applyFilter(to: imageData)
        
        await MainActor.run {
            // Kembali ke main actor untuk update UI
            let result = UIImage(data: processedData)
            updateUI(with: result)
        }
    }
}