5

我希望将自定义属性包装器应用于已经包装的变量,像(A)(B)@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
4

2 回答 2

1

我找到的唯一解决方案是一种解决方法:制作一个内部@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
    }
}

进一步阅读:


编辑:

我也刚刚发现这篇文章可能会提供另一种方法,但我没有时间调查:

于 2021-09-14T19:51:25.840 回答
-1

您提供的任何选项都不能用于使自定义属性包装器的行为与使用 @Published(A 和 B)标记的一样

真正的问题是,我如何观察属性/状态更新变化?

  1. 使用 @Published 包装器,它会自动处理状态更新

  2. 通过实施手动跟踪状态更新

    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 包装器完全工作。

于 2021-03-10T11:58:14.053 回答