为什么用户默认发布者会多次触发

问题描述 投票:0回答:2

我正在订阅内置的用户默认扩展,但它似乎不必要地多次触发。

这是我正在使用的代码:

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]
的触发器,然后再次比较设置的值。

swift combine
2个回答
2
投票

第一个发布的值是您订阅时的初始值,如果您不想接收初始值,可以在选项中指定(它们是

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")

这些值仅发布一次。


0
投票

正如@LuLuGaGa所说,第一个发布的值是初始值。

关于第二次和第三次火灾,它是相同的新值,发布了两次。

这是因为你的计算属性的名称和键是相同的。 我认为这是 KVC/KVO

setValue(:forKey:)
UserDefaults
的方法或 Swift 扩展中的
@objc
属性之间的 Objc 冲突

因此,如果您像这样定义扩展:

extension UserDefaults {
    @objc dynamic var someProperty2: Bool {
        get { bool(forKey: "someProperty") }
        set { set(newValue, forKey: "someProperty") }
    }
}

并像这样订阅:

defaults
    .publisher(for: \.someProperty2, options: [.new])
    .print()
    .sink { print("Sink: \($0)") }
    .store(in: &cancellable)

使用以下方法设置值:

  defaults.someProperty2 = false
  // or
  defaults.set(false, forKey:"someProperty2")

将发布一次值,但使用以下方式设置值:

  defaults.set(false, forKey:"someProperty")

根本不会发布价值

© www.soinside.com 2019 - 2024. All rights reserved.