如何使用 Combine 绑定两个 类 之间的属性?

How to bind properties between two classes with Combine?

如何在引用ObservableObjectclass中的Published的view中做一个类似于binding的机制,但是介于2classes之间?即从classA,调用classB的构造函数,从classA传入prop1。而在classB中prop2存在,这些prop1和prop2应该镜像彼此。

PS: 附上的代码不起作用,只是直观地展示我想要的...

PPS: ClassA 中的 prop1 和 ClassB 中的 prop2,必须按条件发布。它自己的观点也被订阅了。

PPPS:重要的是在 ClassB 中有任何绑定逻辑,以便于重用。

class ClassA: ObservableObject {
    @Published var prop1: Bool = false //When prop1 changed, it must be reflected in prop2
    let classB: ClassB

    init() {
        classB = .init(prop2: _prop1) //Should stay that simple, no setup here, passing only property, ClassB doesn't know about ClassA.
    }
}

class ClassB: ObservableObject {
    @Published var prop2: Bool //When prop2 changed, it must be reflected in prop1

    init(prop2: Published<Bool>) {
        _prop2 = prop2
    }
}

一个简单但通用的解决方案如下所示。

添加以下扩展名:

extension Published.Publisher where Value: Equatable {
    mutating func link(with other: inout Self) {
        removeDuplicates().assign(to: &other)
        other.removeDuplicates().assign(to: &self)
    }
}

这仅适用于符合 Equatable 的值,否则您可能会陷入无限循环。

然后在ClassA的初始化器中调用扩展:

classB.$prop2.link(with: &$prop1)

原始答案

一个简单的解决方案是将以下代码放入 ClassAinit:

$prop1
    .removeDuplicates()
    .assign(to: &classB.$prop2) // mirror changes of prop1 into classB.prop2
classB.$prop2
    .removeDuplicates()
    .assign(to: &$prop1) // mirror changes of classB.prop2 into prop1

但是,我建议从 ClassA 中删除 prop1 并分别通过 classB.prop2classB.$prop2 访问该值。这使得依赖关系更加清晰,并且不需要在两个地方存储和更新值。


评论中有关将逻辑放入 ClassB 的问题的答案

为此,您可以在 ClassB 中添加一个执行链接的函数:

fileprivate func linkProps(to classA: ClassA) {
    $prop2
        .removeDuplicates()
        .assign(to: &classA.$prop1) // mirror changes of prop2 into classA.prop1
    classA.$prop1
        .removeDuplicates()
        .assign(to: &$prop2) // mirror changes of classA.prop1 into prop2
}

然后在 ClassAinit 中调用此函数(以及您需要的任何其他地方):

init() {
    classB = .init(prop2: _prop1)
    classB.linkProps(to: self)
}

回答评论中关于防止 ClassB 知道 ClassA 的问题

这可以通过仅将 属性 包装器的投影值传递到 ClassB 上的函数来实现:

fileprivate func linkProp2(to prop1: inout Published<Bool>.Publisher) {
    $prop2
        .removeDuplicates()
        .assign(to: &prop1) // mirror changes of prop2 into prop1
    prop1
        .removeDuplicates()
        .assign(to: &$prop2) // mirror changes of prop1 into prop2
}

ClassA 初始化程序中的调用将如下所示:

classB.linkProp2(to: &$prop1)