Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -110,9 +110,20 @@ public struct ObservationRegistrar: Sendable {
}
}

internal mutating func cancelAll() {
internal mutating func deinitialize() -> [@Sendable () -> Void] {
var trackers = [@Sendable () -> Void]()
for (keyPath, ids) in lookups {
for id in ids {
if let tracker = observations[id]?.willSetTracker {
trackers.append({
tracker(keyPath)
})
}
}
}
observations.removeAll()
lookups.removeAll()
return trackers
}

internal mutating func willSet(keyPath: AnyKeyPath) -> [@Sendable (AnyKeyPath) -> Void] {
Expand Down Expand Up @@ -157,8 +168,11 @@ public struct ObservationRegistrar: Sendable {
state.withCriticalRegion { $0.cancel(id) }
}

internal func cancelAll() {
state.withCriticalRegion { $0.cancelAll() }
internal func deinitialize() {
let tracking = state.withCriticalRegion { $0.deinitialize() }
for action in tracking {
action()
}
}

internal func willSet<Subject: Observable, Member>(
Expand Down Expand Up @@ -189,7 +203,7 @@ public struct ObservationRegistrar: Sendable {
}

deinit {
context.cancelAll()
context.deinitialize()
}
}

Expand Down
31 changes: 31 additions & 0 deletions test/stdlib/Observation/Observable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,21 @@ final class CowTest {
var container = CowContainer()
}

@Observable
final class DeinitTriggeredObserver {
var property: Int = 3
let deinitTrigger: () -> Void

init(_ deinitTrigger: @escaping () -> Void) {
self.deinitTrigger = deinitTrigger
}

deinit {
deinitTrigger()
}
}


@main
struct Validator {
@MainActor
Expand Down Expand Up @@ -511,6 +526,22 @@ struct Validator {
expectEqual(subject.container.id, startId)
}

suite.test("weak container observation") {
let changed = CapturedState(state: false)
let deinitialized = CapturedState(state: false)
var test = DeinitTriggeredObserver {
deinitialized.state = true
}
withObservationTracking { [weak test] in
_blackHole(test?.property)
} onChange: {
changed.state = true
}
test = DeinitTriggeredObserver { }
expectEqual(deinitialized.state, true)
expectEqual(changed.state, true)
}

runAllTests()
}
}
Expand Down