我希望将自定义属性包装器应用到已包装在
@Published
中的变量,将它们嵌套起来就像 @Custom @Published var myVar
或@Published @Custom var myVar
在 (A) 的情况下,我收到错误
'wrappedValue' is unavailable: @Published is only available on properties of classes
和(B)
error: key path value type 'Int' cannot be converted to contextual type 'Updating<Int>'
两者都不是特别有帮助。有什么想法如何让它发挥作用吗?
import Combine
class A {
@Updating @Published var b: Int
init(b: Int) {
self.b = b
}
}
@propertyWrapper struct Updating<T> {
var wrappedValue: T {
didSet {
print("Update: \(wrappedValue)")
}
}
}
let a = A(b: 1)
let cancellable = a.$b.sink {
print("Published: \($0)")
}
a.b = 2
// Expected output:
// ==> Published: 1
// ==> Published: 2
// ==> Update: 2
您提供的任何选项都不能应用于使自定义属性包装器的行为像用@Published(A和B)标记的那样
真正的问题是,我如何观察属性/状态更新变化?
使用@Published包装器,自动处理状态更新
通过实施
手动跟踪状态更新willSet {objectWillChange.send()}
考虑到选项 1 无法工作,因为您无法应用 2 个属性包装器, 您可以进行手动状态更新跟踪。 为了实现这一点,您需要使您的类符合 ObservableObject 协议。
class A: ObservableObject {
@Updating var b: Int{
willSet {objectWillChange.send()}
}
init(b: Int) {
self.b = b
}
}
现在,只要 var b 发生更改,您的视图就可以刷新,同时您的 @Updating 包装器完全正常工作。
我找到的唯一解决方案是一种解决方法:制作一个自定义
@propertyWrapper
,其中包含 @Published
属性。
示例:
/// Workaround @Published not playing nice with other property wrappers.
/// Use this to replace @Published to temporarily help debug a property being accessed off the main thread.
@propertyWrapper
public class MainThreadPublished<Value> {
@Published
private var value: Value
public var projectedValue: Published<Value>.Publisher {
get {
assert(Thread.isMainThread, "Accessing @MainThread property on wrong thread: \(Thread.current)")
return $value
}
@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *)
set {
assert(Thread.isMainThread, "Accessing @MainThread property on wrong thread: \(Thread.current)")
$value = newValue
}
}
public var wrappedValue: Value {
get {
assert(Thread.isMainThread, "Accessing @MainThread property on wrong thread: \(Thread.current)")
return value
}
set {
assert(Thread.isMainThread, "Accessing @MainThread property on wrong thread: \(Thread.current)")
value = newValue
}
}
public init(wrappedValue value: Value) {
self.value = value
}
public init(initialValue value: Value) {
self.value = value
}
}
进一步阅读:
编辑:
我也刚刚发现这篇文章可能提供替代方法,但我没有时间调查:
我尝试了 MechEthan 的解决方案,但它对我不起作用......并且我通过 Simone 的回答发现我仍然需要发送
objectWillChange
信号。所以我最终得到了这个:
@propertyWrapper
class _CustomPublished<Instance: ObservableObject, Value> where Instance.ObjectWillChangePublisher == ObservableObjectPublisher {
@available(*, unavailable)
var wrappedValue: Value {
get { fatalError() }
set { fatalError() }
}
var projectedValue: Published<Value>.Publisher {
get { $value }
set { $value = newValue }
}
static subscript(
_enclosingInstance instance: Instance,
wrapped _: KeyPath<Instance, Value>,
storage storageKeyPath: KeyPath<Instance, _CustomPublished>
) -> Value {
get { instance[keyPath: storageKeyPath].value }
set {
instance.objectWillChange.send()
instance[keyPath: storageKeyPath].value = newValue
}
}
init(wrappedValue: Value) {
value = wrappedValue
}
@Published private var value: Value
}
extension ObservableObject where Self.ObjectWillChangePublisher == ObservableObjectPublisher {
typealias CustomPublished<T> = _CustomPublished<Self, T>
}
我在我的项目中替换了一些
@Published
,它似乎有效,但不确定是否还有其他隐藏的差异
确实,Swift 在编写属性包装器方面应该做得更好