Section 6/161 menit

6. Closure Isolation dan Capture Semantics

6. Closure Isolation dan Capture Semantics

Closure di Swift mewarisi isolation sesuai konteks tempat ia dibuat — dengan aturan yang berbeda untuk tiap jenis closure.

Aturan Warisan Isolation Closure

swift
@MainActor
class MyController {
    var count = 0

    func setupClosures() {
        // 1. Task {} — mewarisi isolation dari enclosing context (@MainActor)
        Task {
            count += 1  // ✓ @MainActor — tidak perlu await
        }

        // 2. Task.detached {} — TIDAK mewarisi, selalu nonisolated
        Task.detached {
            // count += 1  // ❌ Error: count di @MainActor, ini nonisolated
            await MainActor.run { self.count += 1 }  // ✓ Eksplisit hop
        }

        // 3. @MainActor closure eksplisit
        let mainActorClosure: @MainActor () -> Void = {
            count += 1  // ✓ Eksplisit @MainActor
        }

        // 4. @Sendable closure — nonisolated, bisa dikirim ke thread lain
        let sendableClosure: @Sendable () -> Void = {
            // count += 1  // ❌ Error: @Sendable berarti nonisolated
            print("dari thread lain")
        }

        // 5. Closure sebagai argument — isolation bergantung pada tipe parameter
        DispatchQueue.main.async {
            // Ini BUKAN @MainActor meski jalan di main thread
            // count += 1  // ❌ Error di Swift 6 strict mode
        }
    }
}

Isolation Dalam Async Closure

swift
@MainActor
class FeedViewModel {
    var posts: [Post] = []

    func refresh() async {
        // async closure dalam method @MainActor → tetap @MainActor
        let newPosts = await withTaskGroup(of: Post.self) { group in
            // withTaskGroup closure berjalan di @MainActor
            for id in fetchIDs() {
                group.addTask {
                    // Ini Task child — MEWARISI isolation (@MainActor)
                    // Tapi biasanya mau background, jadi perlu Task.detached di dalam
                    return await Post.fetch(id: id)
                }
            }
            var result: [Post] = []
            for await post in group { result.append(post) }
            return result
        }
        posts = newPosts  // ✓ Kembali ke @MainActor setelah group selesai
    }
}

Capture List dan Isolation

swift
actor DataManager {
    var items: [Item] = []

    func processWithCallback(completion: @escaping @Sendable (Int) -> Void) async {
        let count = items.count  // Capture nilai (bukan self) — lebih aman
        Task.detached {
            completion(count)  // ✓ count adalah Int — Sendable
        }
    }

    func dangerousCapture(completion: @escaping @Sendable () -> Void) async {
        Task.detached { [self] in  // [self] pada actor — aman karena actor Sendable
            await self.items.count  // Harus await untuk akses actor dari luar
        }
    }
}