diff --git a/stdlib/public/Observation/Sources/Observation/ObservationRegistrar.swift b/stdlib/public/Observation/Sources/Observation/ObservationRegistrar.swift index 3c356381bee63..fd61a2d28fc5a 100644 --- a/stdlib/public/Observation/Sources/Observation/ObservationRegistrar.swift +++ b/stdlib/public/Observation/Sources/Observation/ObservationRegistrar.swift @@ -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] { @@ -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( @@ -189,7 +203,7 @@ public struct ObservationRegistrar: Sendable { } deinit { - context.cancelAll() + context.deinitialize() } } diff --git a/test/stdlib/Observation/Observable.swift b/test/stdlib/Observation/Observable.swift index 9fa23325805fc..2f05994060f75 100644 --- a/test/stdlib/Observation/Observable.swift +++ b/test/stdlib/Observation/Observable.swift @@ -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 @@ -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() } }