为啥用户默认发布者多次触发
Posted
技术标签:
【中文标题】为啥用户默认发布者多次触发【英文标题】:Why does User Defaults publisher trigger multiple times为什么用户默认发布者多次触发 【发布时间】:2021-01-30 07:30:53 【问题描述】:我订阅了内置的用户默认扩展,但它似乎不必要地触发了多次。
这是我正在使用的代码:
import Combine
import Foundation
import PlaygroundSupport
extension UserDefaults
@objc var someProperty: Bool
get bool(forKey: "someProperty")
set set(newValue, forKey: "someProperty")
let defaults = UserDefaults.standard
defaults.dictionaryRepresentation().keys
.forEach(defaults.removeObject)
print("Before: \(defaults.someProperty)")
var cancellable = Set<AnyCancellable>()
defaults
.publisher(for: \.someProperty)
.sink print("Sink: \($0)")
.store(in: &cancellable)
defaults.someProperty = true
cancellable.removeAll()
PlaygroundPage.current.needsIndefiniteExecution = true
打印出来:
Before: false
Sink: false
Sink: true
Sink: true
为什么它会触发接收器 3 次而不是只触发一次?
我也许可以理解它在订阅时触发,这令人困惑,因为它似乎不是PassthroughSubject
或任何有关此的文档。然而,真正让我困惑的是它第三次触发。
更新:
这很奇怪,但似乎初始值被考虑到新/旧比较中:
defaults.someProperty = false
defaults.someProperty = true
defaults.someProperty = false
defaults.someProperty = true
print("Initial: \(defaults.someProperty)")
defaults
.publisher(for: \.someProperty, options: [.new])
.sink print("Sink: \($0)")
.store(in: &cancellable)
defaults.someProperty = true
上面会打印出看起来不错的:
Initial: true
Sink: true
但是当初始值与你设置的不同时:
defaults.someProperty = false
defaults.someProperty = true
defaults.someProperty = false
defaults.someProperty = true
defaults.someProperty = false
print("Initial: \(defaults.someProperty)")
defaults
.publisher(for: \.someProperty, options: [.new])
.sink print("Sink: \($0)")
.store(in: &cancellable)
defaults.someProperty = true
上面会奇怪打印:
Initial: false
Sink: true
Sink: true
这是不合理的,因为它将初始值视为[.new]
的触发器,然后再次比较设置的内容。
【问题讨论】:
这发生在单元测试中并导致我的应用出现问题,我将其提取到 Playground 以获得一些帮助。 如果我删除了之前的删除对象部分,如果开火两次而不是 3 次。这很奇怪,因为删除对象出现在订阅之前。就好像它正在重播一切。这是一个什么样的发布者,我怎样才能直观地工作,这个例子真的应该对我触发一次。 ***.com/questions/60386000/…可能重复 感谢您链接该问题!这确实从不同的角度更清楚地说明了这种行为。 【参考方案1】:第一个发布的值是订阅时的初始值,如果不想收到初始值可以在options中指定(它们是NSKeyValueObservingOptions
):
defaults
.publisher(for: \.someProperty, options: [.new])
.sink print("Sink: \($0)")
.store(in: &cancellable)
每个新值确实会发布两次,但您可以删除重复项:
defaults
.publisher(for: \.someProperty, options: [.new])
.removeDuplicates()
.sink print("Sink: \($0)")
.store(in: &cancellable)
这会给你想要的行为。
更新:
如果你这样定义你的扩展:
extension UserDefaults
@objc var someProperty: Bool
bool(forKey: "someProperty")
然后使用以下方法设置值:
defaults.set(false, forKey: "someProperty")
这些值只发布一次。
【讨论】:
不是在那之后发布两次,莫名其妙在订阅前所做的更改,后来由于某种原因触发了它。 如果初始值与您设置的值不同,它似乎会触发两次。因此,如果您设置了[.new]
,从false
开始,然后设置为true
,它将触发第一个新值,然后第二次表示新值已更改。非常混乱和奇怪。当设置为[.new]
时,我希望在订阅发生后获得新值,而不是在将初始值与第一组进行比较时。 removeDuplicates
确实解决了它,但作为一种解决方法。
我不太确定。如果您在defaults.someProperty = true
下添加defaults.someProperty =false
- 在您的初始设置中,它会给出假/真/真/假/假。这一切都非常令人惊讶。
我在原始问题中添加了一个额外的示例。至少有一些疯狂的一致性,但如果初始值与随后的第一个设置值不同,则初始值触发不是很直观。
这很奇怪,就好像它重复了两次这样你才能得到消息;-)以上是关于为啥用户默认发布者多次触发的主要内容,如果未能解决你的问题,请参考以下文章
为啥在使用 Caliburn Micro Conductor.OneActive 时,Blend Interaction 事件触发器会多次触发?