gpt4 book ai didi

ios - 在 didSet 之后观察 Swift Combine 中 @Published var 的变化?

转载 作者:行者123 更新时间:2023-12-05 00:52:28 24 4
gpt4 key购买 nike

假设我们有以下使用 Swift 编写的使用 Combine 的代码:

import UIKit
import Combine

class Test {
@Published var array: [Int] = [] {
willSet {
print("willSet \(newValue.count)")
}
didSet {
print("didSet \(array.count)")
}
}
}

var test = Test()
var subscriber = test.$array.sink { values in
print("arrayCount: \(test.array.count) valuesCount: \(values.count)")
}

print("1 arrayCount \(test.array.count)")
test.array = [1, 2, 3]
print("2 arrayCount \(test.array.count)")
test.array = [1]
print("3 arrayCount \(test.array.count)")

此代码在控制台上打印以下结果(可以在 Playground 上快速测试):

arrayCount: 0 valuesCount: 0
1 arrayCount 0
willSet 3
arrayCount: 0 valuesCount: 3
didSet 3
2 arrayCount 3
willSet 1
arrayCount: 3 valuesCount: 1
didSet 1
3 arrayCount 1

正如我们所见,sink 方法的代码是在给定属性的 willSet 和 didSet 之前执行的。现在我的问题是:有没有办法创建这个发布者或订阅它,使得给 sink 的代码在 didSet 之后而不是在它之前执行(这样当从 sink 打印时 arrayCount 和 valuesCount 将是相同的在上面的例子中)?

最佳答案

Published.Publisher 使用 willSet 为包装的属性发出值。不幸的是,您无法更改此行为,唯一的解决方案是实现您自己的使用 didSet 而不是 willSet 的属性包装器。

/// A type that publishes changes about its `wrappedValue` property _after_ the property has changed (using `didSet` semantics).
/// Reimplementation of `Combine.Published`, which uses `willSet` semantics.
@propertyWrapper
public class PostPublished<Value> {
/// A `Publisher` that emits the new value of `wrappedValue` _after it was_ mutated (using `didSet` semantics).
public let projectedValue: AnyPublisher<Value, Never>
/// A `Publisher` that fires whenever `wrappedValue` _was_ mutated. To access the new value of `wrappedValue`, access `wrappedValue` directly, this `Publisher` only signals a change, it doesn't contain the changed value.
public let valueDidChange: AnyPublisher<Void, Never>
private let didChangeSubject: PassthroughSubject<Value, Never>
public var wrappedValue: Value { didSet { didChangeSubject.send(wrappedValue) } }

public init(wrappedValue: Value) {
self.wrappedValue = wrappedValue
let didChangeSubject = PassthroughSubject<Value, Never>()
self.didChangeSubject = didChangeSubject
self.projectedValue = didChangeSubject.eraseToAnyPublisher()
self.valueDidChange = didChangeSubject.voidPublisher()
}
}

public extension Publisher {
/// Maps the `Output` of its upstream to `Void` and type erases its upstream to `AnyPublisher`.
func voidPublisher() -> AnyPublisher<Void, Failure> {
map { _ in Void() }
.eraseToAnyPublisher()
}
}

您可以像观察 @Published 一样观察 @PostPublished

private class ModelWithPostPublished<Value> {
@PostPublished var value: Value

init(value: Value) {
self.value = value
}
}

ModelWithPostPublished(value: "original").$value.sink { print("value WAS set to \($0) }.store(in: &subscriptions)

关于ios - 在 didSet 之后观察 Swift Combine 中 @Published var 的变化?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69978018/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com