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