@@ -61,7 +61,11 @@ internal struct StoreContext {
6161 else {
6262 let cache = makeNewCache ( of: atom, for: key, override: override)
6363 notifyUpdateToObservers ( )
64- checkRelease ( for: key)
64+
65+ if checkRelease ( for: key) {
66+ notifyUpdateToObservers ( )
67+ }
68+
6569 return cache. value
6670 }
6771 }
@@ -73,7 +77,6 @@ internal struct StoreContext {
7377
7478 if let cache = lookupCache ( of: atom, for: key) {
7579 update ( atom: atom, for: key, value: value, cache: cache, order: . newValue)
76- checkRelease ( for: key)
7780 }
7881 }
7982
@@ -86,7 +89,6 @@ internal struct StoreContext {
8689 var value = cache. value
8790 body ( & value)
8891 update ( atom: atom, for: key, value: value, cache: cache, order: . newValue)
89- checkRelease ( for: key)
9092 }
9193 }
9294
@@ -129,17 +131,14 @@ internal struct StoreContext {
129131 location: container. location,
130132 requiresObjectUpdate: requiresObjectUpdate,
131133 notifyUpdate: notifyUpdate
132- ) {
133- let store = getStore ( )
134- // Unsubscribe and release if it's no longer used.
135- store. state. subscriptions [ key] ? . removeValue ( forKey: container. key)
136- notifyUpdateToObservers ( )
137- checkRelease ( for: key)
138- }
134+ )
139135 let isInserted = store. state. subscriptions [ key, default: [ : ] ] . updateValue ( subscription, forKey: container. key) == nil
140136
141137 // Register the subscription to both the store and the container.
142138 container. subscriptions [ key] = subscription
139+ container. unsubscribe = { keys in
140+ unsubscribe ( keys, for: container. key)
141+ }
143142
144143 if isInserted || cache == nil {
145144 notifyUpdateToObservers ( )
@@ -165,8 +164,12 @@ internal struct StoreContext {
165164 if let cache = lookupCache ( of: atom, for: key) {
166165 update ( atom: atom, for: key, value: value, cache: cache, order: . newValue)
167166 }
167+ else {
168+ // Release the temporarily created state.
169+ // Do not notify update to observers here because refresh doesn't create a new cache.
170+ release ( for: key)
171+ }
168172
169- checkRelease ( for: key)
170173 return value
171174 }
172175
@@ -178,15 +181,16 @@ internal struct StoreContext {
178181 if let cache = lookupCache ( of: atom, for: key) {
179182 let newCache = makeNewCache ( of: atom, for: key, override: override)
180183 update ( atom: atom, for: key, value: newCache. value, cache: cache, order: . newValue)
181- checkRelease ( for: key)
182184 }
183185 }
184186
185187 @usableFromInline
186188 func unwatch( _ atom: some Atom , container: SubscriptionContainer . Wrapper ) {
187189 let override = lookupOverride ( of: atom)
188190 let key = AtomKey ( atom, overrideScopeKey: override? . scopeKey)
189- container. subscriptions. removeValue ( forKey: key) ? . unsubscribe ( )
191+
192+ container. subscriptions. removeValue ( forKey: key)
193+ unsubscribe ( [ key] , for: container. key)
190194 }
191195
192196 @usableFromInline
@@ -223,7 +227,7 @@ internal struct StoreContext {
223227
224228 // Release dependencies that are no longer dependent.
225229 if let dependencies = obsoletedDependencies [ key] {
226- checkReleaseDependencies ( dependencies , for : key )
230+ detach ( key , fromDependencies : dependencies )
227231 }
228232
229233 // Notify updates only for the subscriptions of restored atoms.
@@ -233,6 +237,8 @@ internal struct StoreContext {
233237 }
234238 }
235239 }
240+
241+ notifyUpdateToObservers ( )
236242 }
237243}
238244
@@ -267,8 +273,10 @@ private extension StoreContext {
267273 let dependencies = store. graph. dependencies [ key] ?? [ ]
268274 let obsoletedDependencies = oldDependencies. subtracting ( dependencies)
269275
270- // Check if the dependencies that are no longer used and release them if possible.
271- checkReleaseDependencies ( obsoletedDependencies, for: key)
276+ if !obsoletedDependencies. isEmpty {
277+ detach ( key, fromDependencies: obsoletedDependencies)
278+ notifyUpdateToObservers ( )
279+ }
272280 }
273281
274282 // Register the transaction state so it can be terminated from anywhere.
@@ -358,6 +366,17 @@ private extension StoreContext {
358366 }
359367 }
360368
369+ func unsubscribe( _ keys: [ AtomKey ] , for subscriptionKey: SubscriptionKey ) {
370+ let store = getStore ( )
371+
372+ for key in keys {
373+ store. state. subscriptions [ key] ? . removeValue ( forKey: subscriptionKey)
374+ checkRelease ( for: key)
375+ }
376+
377+ notifyUpdateToObservers ( )
378+ }
379+
361380 func invalidate( for key: AtomKey ) -> Set < AtomKey > {
362381 let store = getStore ( )
363382
@@ -378,14 +397,11 @@ private extension StoreContext {
378397 store. state. states. removeValue ( forKey: key)
379398 store. state. subscriptions. removeValue ( forKey: key)
380399
381- // Check if the dependencies are releasable.
382- checkReleaseDependencies ( dependencies, for: key)
383-
384- // Notify release.
385- notifyUpdateToObservers ( )
400+ detach ( key, fromDependencies: dependencies)
386401 }
387402
388- func checkRelease( for key: AtomKey ) {
403+ @discardableResult
404+ func checkRelease( for key: AtomKey ) -> Bool {
389405 let store = getStore ( )
390406
391407 // The condition under which an atom may be released are as follows:
@@ -398,16 +414,16 @@ private extension StoreContext {
398414 lazy var shouldRelease = !shouldKeepAlive && isChildrenEmpty && isSubscriptionEmpty
399415
400416 guard shouldRelease else {
401- return
417+ return false
402418 }
403419
404420 release ( for: key)
421+ return true
405422 }
406423
407- func checkReleaseDependencies ( _ dependencies : Set < AtomKey > , for key : AtomKey ) {
424+ func detach ( _ key : AtomKey , fromDependencies dependencies : Set < AtomKey > ) {
408425 let store = getStore ( )
409426
410- // Recursively release dependencies while unlinking the dependent.
411427 for dependency in dependencies {
412428 store. graph. children [ dependency] ? . remove ( key)
413429 checkRelease ( for: dependency)
@@ -480,6 +496,7 @@ private extension StoreContext {
480496
481497 // Release the invalid registration as a fallback.
482498 release ( for: key)
499+ notifyUpdateToObservers ( )
483500 return makeState ( )
484501 }
485502
@@ -509,6 +526,7 @@ private extension StoreContext {
509526
510527 // Release the invalid registration as a fallback.
511528 release ( for: key)
529+ notifyUpdateToObservers ( )
512530 return nil
513531 }
514532
0 commit comments