Section 3/132 menit

3. nonisolated dan isolated Parameter

3. nonisolated dan isolated Parameter

3.1 Teori: Biaya Isolation

Setiap akses ke isolated property atau method membutuhkan actor hop — sebuah context switch ke executor actor. Ini memang lebih efisien dari thread switch konvensional (karena cooperative), tapi tetap ada overhead: bookkeeping, queue operation, dan potential task suspension.

Untuk data yang tidak memerlukan perlindungan (misalnya immutable constants atau computed properties murni), overhead ini tidak diperlukan. nonisolated adalah cara untuk menyatakan: "bagian ini aman diakses langsung, tanpa perlu melalui antrian actor."

3.2 nonisolated — Opt-Out dari Isolation

nonisolated memungkinkan method/property diakses tanpa await dari mana saja, tapi sebagai konsekuensi tidak boleh mengakses isolated state actor:

swift
actor UserProfile {
    var name: String          // isolated — bisa berubah, butuh perlindungan
    var email: String         // isolated — sama
    let id: UUID              // immutable — tidak bisa berubah, tidak butuh perlindungan
    
    init(name: String, email: String) {
        self.name = name
        self.email = email
        self.id = UUID()
    }
    
    // Bisa dipanggil tanpa await dari mana saja
    // Tidak ada overhead actor hop — langsung eksekusi
    nonisolated func displayID() -> String {
        return id.uuidString  // OK: id adalah let (immutable)
    }
    
    // ❌ ERROR: name adalah var — bisa berubah, tidak aman tanpa isolation
    // nonisolated func displayName() -> String { name }
}

// Penggunaan:
let profile = UserProfile(name: "Ari", email: "ari@example.com")
let idString = profile.displayID()  // synchronous! tidak perlu await

Kapan gunakan nonisolated:

  • Method hanya mengakses let property (immutable)
  • Method hanya mengakses property lain yang juga nonisolated
  • Pure functions / computed properties tanpa mutable state
  • Protocol conformances yang harus synchronous (misalnya CustomStringConvertible)

Kapan TIDAK gunakan nonisolated:

  • Jika method mengakses var property — compiler akan error
  • Jika method memanggil isolated method lain — butuh await
  • Jika ada keraguan — biarkan actor melindunginya
swift
actor Logger {
    private var logs: [String] = []
    private let prefix: String  // immutable
    
    // nonisolated OK: hanya akses let
    nonisolated var description: String {
        "Logger[\(prefix)]"  // CustomStringConvertible tanpa await
    }
    
    // Harus isolated: mutasi logs
    func log(_ message: String) {
        logs.append("[\(prefix)] \(message)")
    }
    
    // nonisolated OK karena hanya akses let + pure computation
    nonisolated func format(_ message: String) -> String {
        "[\(prefix)] \(message)"
    }
}

3.3 isolated Parameter — Utility Function dalam Domain Actor

isolated parameter memungkinkan fungsi bebas (bukan method actor) untuk berjalan di dalam isolation domain actor tertentu:

swift
actor Database {
    var records: [String: String] = [:]
}

// Fungsi ini bukan method Database, tapi berjalan DALAM isolation domain 'db'
// Artinya: tidak butuh await untuk akses db.records
func insertRecord(key: String, value: String, into db: isolated Database) {
    db.records[key] = value  // OK: kita sedang dalam isolation domain db
}

// Penggunaan — butuh await karena harus hop ke Database executor
let db = Database()
Task {
    await insertRecord(key: "user_1", value: "Ari", into: db)
}

Mengapa ini berguna? Bayangkan kamu punya banyak utility functions yang logikanya terkait erat dengan sebuah actor, tapi tidak ingin membuat actor membengkak dengan banyak method. isolated parameter memisahkan concerns sambil tetap mendapat keamanan isolation.

Perbandingan pendekatan:

swift
// Pendekatan 1: method di dalam actor (meningkatkan ukuran actor)
actor DataProcessor {
    var data: [Int] = []
    func filterEven() { data = data.filter { $0.isMultiple(of: 2) } }
    func filterOdd()  { data = data.filter { !$0.isMultiple(of: 2) } }
    func sort()       { data.sort() }
    // ... actor bisa jadi sangat besar
}

// Pendekatan 2: isolated parameter (actor tetap kecil, logic di luar)
actor DataProcessor {
    var data: [Int] = []
}

func filterEven(processor: isolated DataProcessor) {
    processor.data = processor.data.filter { $0.isMultiple(of: 2) }
}

func sort(processor: isolated DataProcessor) {
    processor.data.sort()
}

Kapan gunakan isolated parameter:

  • Utility functions yang logikanya terkait satu actor tapi bukan bagian inti actor
  • Extension functions yang perlu akses isolated state
  • Testing helpers yang perlu inspect/modify actor state

Kapan TIDAK gunakan isolated parameter:

  • Jika function butuh akses ke dua actor berbeda — tidak bisa punya dua isolated parameter dari actor berbeda sekaligus (deadlock potential)
  • Jika logic memang bagian inti dari actor — jadikan method biasa saja