Improve performance of `Defaults.updates()`
This commit is contained in:
parent
23e0a4497e
commit
1064186b3d
|
@ -239,7 +239,7 @@ extension Defaults {
|
||||||
initial: Bool = true
|
initial: Bool = true
|
||||||
) -> AsyncStream<Value> { // TODO: Make this `some AsyncSequence<Value>` when Swift 6 is out.
|
) -> AsyncStream<Value> { // TODO: Make this `some AsyncSequence<Value>` when Swift 6 is out.
|
||||||
.init { continuation in
|
.init { continuation in
|
||||||
let observation = UserDefaultsKeyObservation(object: key.suite, key: key.name) { change in
|
let observation = UserDefaultsKeyObservation2(object: key.suite, key: key.name) { change in
|
||||||
// TODO: Use the `.deserialize` method directly.
|
// TODO: Use the `.deserialize` method directly.
|
||||||
let value = KeyChange(change: change, defaultValue: key.defaultValue).newValue
|
let value = KeyChange(change: change, defaultValue: key.defaultValue).newValue
|
||||||
continuation.yield(value)
|
continuation.yield(value)
|
||||||
|
@ -275,7 +275,7 @@ extension Defaults {
|
||||||
) -> AsyncStream<Void> { // TODO: Make this `some AsyncSequence<Value>` when Swift 6 is out.
|
) -> AsyncStream<Void> { // TODO: Make this `some AsyncSequence<Value>` when Swift 6 is out.
|
||||||
.init { continuation in
|
.init { continuation in
|
||||||
let observations = keys.indexed().map { index, key in
|
let observations = keys.indexed().map { index, key in
|
||||||
let observation = UserDefaultsKeyObservation(object: key.suite, key: key.name) { _ in
|
let observation = UserDefaultsKeyObservation2(object: key.suite, key: key.name) { _ in
|
||||||
continuation.yield()
|
continuation.yield()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -180,7 +180,7 @@ extension Defaults {
|
||||||
}
|
}
|
||||||
|
|
||||||
guard
|
guard
|
||||||
selfObject == object as? NSObject,
|
selfObject == (object as? NSObject),
|
||||||
let change
|
let change
|
||||||
else {
|
else {
|
||||||
return
|
return
|
||||||
|
@ -191,6 +191,69 @@ extension Defaults {
|
||||||
guard !updatingValuesFlag else {
|
guard !updatingValuesFlag else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
callback(BaseChange(change: change))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Same as the above, but without the lifetime utilities, which slows down invalidation and we don't need them for `.updates()`.
|
||||||
|
final class UserDefaultsKeyObservation2: NSObject {
|
||||||
|
typealias Callback = (BaseChange) -> Void
|
||||||
|
|
||||||
|
private weak var object: UserDefaults?
|
||||||
|
private let key: String
|
||||||
|
private let callback: Callback
|
||||||
|
private var isObserving = false
|
||||||
|
|
||||||
|
init(object: UserDefaults, key: String, callback: @escaping Callback) {
|
||||||
|
self.object = object
|
||||||
|
self.key = key
|
||||||
|
self.callback = callback
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
invalidate()
|
||||||
|
}
|
||||||
|
|
||||||
|
func start(options: ObservationOptions) {
|
||||||
|
object?.addObserver(self, forKeyPath: key, options: options.toNSKeyValueObservingOptions, context: nil)
|
||||||
|
isObserving = true
|
||||||
|
}
|
||||||
|
|
||||||
|
func invalidate() {
|
||||||
|
if isObserving {
|
||||||
|
object?.removeObserver(self, forKeyPath: key, context: nil)
|
||||||
|
isObserving = false
|
||||||
|
}
|
||||||
|
|
||||||
|
object = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// swiftlint:disable:next block_based_kvo
|
||||||
|
override func observeValue(
|
||||||
|
forKeyPath keyPath: String?,
|
||||||
|
of object: Any?,
|
||||||
|
change: [NSKeyValueChangeKey: Any]?, // swiftlint:disable:this discouraged_optional_collection
|
||||||
|
context: UnsafeMutableRawPointer?
|
||||||
|
) {
|
||||||
|
guard let selfObject = self.object else {
|
||||||
|
invalidate()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
guard
|
||||||
|
selfObject == (object as? NSObject),
|
||||||
|
let change
|
||||||
|
else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let key = preventPropagationThreadDictionaryKey
|
||||||
|
let updatingValuesFlag = (Thread.current.threadDictionary[key] as? Bool) ?? false
|
||||||
|
guard !updatingValuesFlag else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
callback(BaseChange(change: change))
|
callback(BaseChange(change: change))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
import Combine
|
import Combine
|
||||||
#if DEBUG
|
#if DEBUG && canImport(OSLog)
|
||||||
#if canImport(OSLog)
|
|
||||||
import OSLog
|
import OSLog
|
||||||
#endif
|
#endif
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
extension String {
|
extension String {
|
||||||
|
|
Loading…
Reference in New Issue