Skip to content

Commit 83e6f32

Browse files
committed
[Observation] Restrict deinitialization fired events to be limited to one per registrar and send a \Self.self as the keypath to avoid property confusions
1 parent e95b857 commit 83e6f32

File tree

2 files changed

+20
-14
lines changed

2 files changed

+20
-14
lines changed

stdlib/public/Observation/Sources/Observation/ObservationRegistrar.swift

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -110,20 +110,27 @@ public struct ObservationRegistrar: Sendable {
110110
}
111111
}
112112

113-
internal mutating func deinitialize() -> [@Sendable () -> Void] {
114-
var trackers = [@Sendable () -> Void]()
113+
internal mutating func deinitialize() -> (@Sendable () -> Void)? {
114+
func extractSelf<T>(_ ty: T.Type) -> AnyKeyPath {
115+
return \T.self
116+
}
117+
118+
var tracker: (@Sendable () -> Void)?
115119
for (keyPath, ids) in lookups {
116120
for id in ids {
117-
if let tracker = observations[id]?.willSetTracker {
118-
trackers.append({
119-
tracker(keyPath)
120-
})
121+
if let found = observations[id]?.willSetTracker {
122+
// convert the keyPath into its \Self.self version
123+
let selfKp = _openExistential(type(of: keyPath).rootType, do: extractSelf)
124+
found = {
125+
tracker(selfKp)
126+
}
127+
break
121128
}
122129
}
123130
}
124131
observations.removeAll()
125132
lookups.removeAll()
126-
return trackers
133+
return tracker
127134
}
128135

129136
internal mutating func willSet(keyPath: AnyKeyPath) -> [@Sendable (AnyKeyPath) -> Void] {
@@ -169,10 +176,7 @@ public struct ObservationRegistrar: Sendable {
169176
}
170177

171178
internal func deinitialize() {
172-
let tracking = state.withCriticalRegion { $0.deinitialize() }
173-
for action in tracking {
174-
action()
175-
}
179+
state.withCriticalRegion { $0.deinitialize() }?()
176180
}
177181

178182
internal func willSet<Subject: Observable, Member>(

test/stdlib/Observation/Observable.swift

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,7 @@ final class CowTest {
290290
@Observable
291291
final class DeinitTriggeredObserver {
292292
var property: Int = 3
293+
var property2: Int = 4
293294
let deinitTrigger: () -> Void
294295

295296
init(_ deinitTrigger: @escaping () -> Void) {
@@ -528,17 +529,18 @@ struct Validator {
528529

529530
suite.test("weak container observation") {
530531
let changed = CapturedState(state: false)
531-
let deinitialized = CapturedState(state: false)
532+
let deinitialized = CapturedState(state: 0)
532533
var test = DeinitTriggeredObserver {
533-
deinitialized.state = true
534+
deinitialized.state += 1
534535
}
535536
withObservationTracking { [weak test] in
536537
_blackHole(test?.property)
538+
_blackHole(test?.property2)
537539
} onChange: {
538540
changed.state = true
539541
}
540542
test = DeinitTriggeredObserver { }
541-
expectEqual(deinitialized.state, true)
543+
expectEqual(deinitialized.state, 1) // ensure only one invocation is done per deinitialization
542544
expectEqual(changed.state, true)
543545
}
544546

0 commit comments

Comments
 (0)