Skip to content

Commit a435032

Browse files
committed
Reduce number of times to send updates to observers
1 parent 7c4e3bb commit a435032

File tree

6 files changed

+197
-102
lines changed

6 files changed

+197
-102
lines changed

Sources/Atoms/Core/StoreContext.swift

Lines changed: 38 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -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
}
@@ -127,19 +131,14 @@ internal struct StoreContext {
127131
location: container.location,
128132
requiresObjectUpdate: requiresObjectUpdate,
129133
notifyUpdate: notifyUpdate
130-
) {
131-
let store = getStore()
132-
// Unsubscribe and release if it's no longer used.
133-
store.state.subscriptions[key]?.removeValue(forKey: container.key)
134-
135-
if !checkRelease(for: key) {
136-
notifyUpdateToObservers()
137-
}
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,11 @@ 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+
// Do not notify update observers here because refresh doesn't create a new cache.
169+
checkRelease(for: key)
170+
}
168171

169-
checkRelease(for: key)
170172
return value
171173
}
172174

@@ -185,7 +187,9 @@ internal struct StoreContext {
185187
func unwatch(_ atom: some Atom, container: SubscriptionContainer.Wrapper) {
186188
let override = lookupOverride(of: atom)
187189
let key = AtomKey(atom, overrideScopeKey: override?.scopeKey)
188-
container.subscriptions.removeValue(forKey: key)?.unsubscribe()
190+
191+
container.subscriptions.removeValue(forKey: key)
192+
unsubscribe([key], for: container.key)
189193
}
190194

191195
@usableFromInline
@@ -222,7 +226,7 @@ internal struct StoreContext {
222226

223227
// Release dependencies that are no longer dependent.
224228
if let dependencies = obsoletedDependencies[key] {
225-
checkReleaseDependencies(dependencies, for: key)
229+
detach(key, fromDependencies: dependencies)
226230
}
227231

228232
// Notify updates only for the subscriptions of restored atoms.
@@ -232,6 +236,8 @@ internal struct StoreContext {
232236
}
233237
}
234238
}
239+
240+
notifyUpdateToObservers()
235241
}
236242
}
237243

@@ -266,8 +272,10 @@ private extension StoreContext {
266272
let dependencies = store.graph.dependencies[key] ?? []
267273
let obsoletedDependencies = oldDependencies.subtracting(dependencies)
268274

269-
// Check if the dependencies that are no longer used and release them if possible.
270-
checkReleaseDependencies(obsoletedDependencies, for: key)
275+
if !obsoletedDependencies.isEmpty {
276+
detach(key, fromDependencies: obsoletedDependencies)
277+
notifyUpdateToObservers()
278+
}
271279
}
272280

273281
// Register the transaction state so it can be terminated from anywhere.
@@ -357,6 +365,17 @@ private extension StoreContext {
357365
}
358366
}
359367

368+
func unsubscribe(_ keys: [AtomKey], for subscriptionKey: SubscriptionKey) {
369+
let store = getStore()
370+
371+
for key in keys {
372+
store.state.subscriptions[key]?.removeValue(forKey: subscriptionKey)
373+
checkRelease(for: key)
374+
}
375+
376+
notifyUpdateToObservers()
377+
}
378+
360379
func invalidate(for key: AtomKey) -> Set<AtomKey> {
361380
let store = getStore()
362381

@@ -377,11 +396,7 @@ private extension StoreContext {
377396
store.state.states.removeValue(forKey: key)
378397
store.state.subscriptions.removeValue(forKey: key)
379398

380-
// Check if the dependencies are releasable.
381-
checkReleaseDependencies(dependencies, for: key)
382-
383-
// Notify release.
384-
notifyUpdateToObservers()
399+
detach(key, fromDependencies: dependencies)
385400
}
386401

387402
@discardableResult
@@ -405,10 +420,9 @@ private extension StoreContext {
405420
return true
406421
}
407422

408-
func checkReleaseDependencies(_ dependencies: Set<AtomKey>, for key: AtomKey) {
423+
func detach(_ key: AtomKey, fromDependencies dependencies: Set<AtomKey>) {
409424
let store = getStore()
410425

411-
// Recursively release dependencies while unlinking the dependent.
412426
for dependency in dependencies {
413427
store.graph.children[dependency]?.remove(key)
414428
checkRelease(for: dependency)
@@ -481,6 +495,7 @@ private extension StoreContext {
481495

482496
// Release the invalid registration as a fallback.
483497
release(for: key)
498+
notifyUpdateToObservers()
484499
return makeState()
485500
}
486501

@@ -510,6 +525,7 @@ private extension StoreContext {
510525

511526
// Release the invalid registration as a fallback.
512527
release(for: key)
528+
notifyUpdateToObservers()
513529
return nil
514530
}
515531

Sources/Atoms/Core/Subscription.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,4 @@ internal struct Subscription {
33
let location: SourceLocation
44
let requiresObjectUpdate: Bool
55
let notifyUpdate: () -> Void
6-
let unsubscribe: () -> Void
76
}

Sources/Atoms/Core/SubscriptionContainer.swift

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,13 @@
22
@MainActor
33
internal final class SubscriptionContainer {
44
private var subscriptions = [AtomKey: Subscription]()
5+
private var unsubscribe: (([AtomKey]) -> Void)?
56
private let token = SubscriptionKey.Token()
67

78
nonisolated init() {}
89

910
deinit {
10-
for subscription in ContiguousArray(subscriptions.values) {
11-
subscription.unsubscribe()
12-
}
11+
unsubscribe?(Array(subscriptions.keys))
1312
}
1413

1514
func wrapper(location: SourceLocation) -> Wrapper {
@@ -31,6 +30,11 @@ internal extension SubscriptionContainer {
3130
nonmutating set { container?.subscriptions = newValue }
3231
}
3332

33+
var unsubscribe: (([AtomKey]) -> Void)? {
34+
get { container?.unsubscribe }
35+
nonmutating set { container?.unsubscribe = newValue }
36+
}
37+
3438
init(
3539
_ container: SubscriptionContainer,
3640
token: SubscriptionKey.Token,

0 commit comments

Comments
 (0)