我正在try 创建一个类似于CombinePublished的属性包装器(用于我的项目需要),但能够通过向发布者发送存储在projectedValue中的值来修改包装的属性,如下所示:

// in class
@PublishedMutable var foo = "foo"

$foo.send("bar")
// ...

以下是属性包装的代码:

@propertyWrapper
struct PublishedMutable<Value> {
    static subscript<T: ObservableObject>(
        _enclosingInstance instance: T,
        wrapped wrappedKeyPath: ReferenceWritableKeyPath<T, Value>,
        storage storageKeyPath: ReferenceWritableKeyPath<T, Self>
    ) -> Value {
        get {
            instance[keyPath: storageKeyPath].storage
        }
        set {
            let publisher = instance.objectWillChange
            // This assumption is definitely not safe to make in
            // production code, but it's fine for this demo purpose:
            (publisher as? ObservableObjectPublisher)?.send()
            
            instance[keyPath: storageKeyPath].storage = newValue
        }
    }
    
    @available(*, unavailable,
                message: "@PublishedMutable can only be applied to classes"
    )
    var wrappedValue: Value {
        get { fatalError() }
        set { fatalError() }
    }
    
    private var storage: Value{
        get { publisher.value }
        set { publisher.send(newValue) }
    }
    typealias Publisher = CurrentValueSubject<Value, Never>

    var projectedValue: Publisher { self.publisher }

    private var publisher: Publisher
    init(initialValue: Value) {
        self.publisher = Publisher(initialValue)
    }
    
    init(wrappedValue: Value) {
        self.init(initialValue: wrappedValue)
    }
}

下面是我用来测试其功能的playground 代码:

class AmogusComic: ObservableObject {
    @PublishedMutable var currentPageContent = "S O S"
    
    private var cancellables: Set<AnyCancellable> = []
    
    func connect() {
        $currentPageContent.sink { newPage in
            print(newPage)
        }
        .store(in: &cancellables)
        objectWillChange.sink { _ in
            print("Update")
        }
        .store(in: &cancellables)
    }
}

let amogusComic = AmogusComic()
DispatchQueue.main.async {
    // connect publishers and print initial content. prints "S O S"
    amogusComic.connect()
    
    // change the value of current page via setting a property, the amogusComic class will reactively print "S U S" after
    amogusComic.currentPageContent = "S U S"
    
    // take a property publisher and send new value to publisher, the amogusComic class will reactively print "A M O G U S" after
    amogusComic.$currentPageContent.send("A M O G U S")
}

PlaygroundPage.current.needsIndefiniteExecution = true

只需通过直接修改属性和将值发送到Publisher来修改值就可以完美地工作. 但我也想在任何时候值发生变化时呼叫objectWillChange.send() 问题是,只有在直接修改包装属性时才会调用它,这是不正确的行为.

测试代码输出如下

S O S
Update
S U S
A M O G U S

当我认为它应该输出这个

S O S
Update
S U S
Update
A M O G U S

正如您在上面的代码中看到的,我使用_enclosingInstance个静态下标来获取ObservableObject并调用objectWillChange,但事实证明,只有在直接修改属性时,SWIFT才会调用该下标(amogusComic.currentPageContent = "S U S")

得到Mirror(reflecting: self).superclassMirror也不起作用,superclassMirror总是返回零

我能不能得到_enclosingInstance静态下标之外的ObservableObject?因此,我还可以在将值发送到发布者时调用objectWillChange

推荐答案

不是的.我们无法访问等同于projectedValue的静态下标.这是苹果的秘密,Published如何订阅其父对象的objectWillChange,而不需要首先调用它的getter或setter.

你只能重新创建(你有)PublishedprojectedValue如果是the class you use it with is not an ObservableObject.

这就是说,如果你的意图仅仅是有一个send的方法,那么就没有必要这样做.

final class Object: ObservableObject {
  @Published var published = ""
}

let object = Object()
object.objectWillChange.sink { print("objectWillChange") }
object.$published.send("new value")
// objectWillChange
object.published // "new value"
public extension Published.Publisher {
  mutating func send(_ value: Value) {
    Just(value).assign(to: &self)
  }
}

Swift相关问答推荐

UITableView未显示类数组数据

从AppKit打开SwiftUI设置

文件命名&NumberForMatter+扩展名&:含义?

SwiftUI-如何使用剩余时间制作倒计时计时器

将matchedGeometryEffect 应用于列表视图内的视图时,列表视图中的项目会受到影响 - SwiftUI

找出touch 点的屏幕亮度

循环字典中的数组

可以将属性名称传递给 Swift 中的函数吗?

解码 JSON 时 Swift Struct Decoder 初始化程序错误

SwiftUI View .tint(_ Color) 方法不起作用

状态变量更改后,SwiftUI 视图不会更改

在 Swift 中为(递归)扩展提供默认实例参数

Swift 中的Combine和didSet有什么区别?

Swift Swiftui - 将 colored颜色 保存到 UserDefaults 并从 @AppStorage 使用它

引用类型(类)不需要但 struct 需要的可识别 id

在 Swift 中获取双精度的小数部分

'NSLog' 不可用:可变参数函数在 swift 中不可用

Swift 自定义字体 Xcode

在 Swift 框架中加载资源(例如故事板)

如何判断 firebase 数据库值是否存在?