mrp*_*w69 7 swift combine property-wrapper property-wrapper-published swift-property-wrapper
我正在尝试制作一个类似于 的属性包装器Combine(Published满足我的项目需求),但能够通过向发布者发送存储在 中的值来修改包装的属性projectedValue,如下所示:
// in class
@PublishedMutable var foo = "foo"
$foo.send("bar")
// ...
Run Code Online (Sandbox Code Playgroud)
这是属性包装器的代码:
@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)
}
}
Run Code Online (Sandbox Code Playgroud)
这是我用来测试其功能的游乐场代码:
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
Run Code Online (Sandbox Code Playgroud)
只需通过直接修改属性并将值发送给发布者来修改值即可完美完成。但我也想objectWillChange.send()在值更改时调用。问题是,只有在直接修改包装的属性时才会调用它,这是不正确的行为。
测试代码输出这个
S O S
Update
S U S
A M O G U S
Run Code Online (Sandbox Code Playgroud)
当我认为应该输出这个时
S O S
Update
S U S
Update
A M O G U S
Run Code Online (Sandbox Code Playgroud)
正如你在上面的代码中看到的,我使用_enclosingInstance静态下标来获取ObservableObject和调用objectWillChange,但事实证明,Swift 仅在直接修改属性时才调用该下标 ( amogusComic.currentPageContent = "S U S")
获取Mirror(reflecting: self).superclassMirror也不起作用,superclassMirror总是返回nil
我能以某种方式得到静态下标的ObservableObject外部吗?所以我也可以在向发布者发送值时_enclosingInstance调用objectWillChange
小智 2
不可以。我们无法访问相当于 的静态下标projectedValue。Apple 的秘密是,a 如何Published订阅其父对象的objectWillChange,而不需要先调用其 getter 或 setter。
如果您使用它的Published类不是.projectedValueObservableObject
也就是说,如果您的目的只是想要一种方法,则没有必要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"
Run Code Online (Sandbox Code Playgroud)
public extension Published.Publisher {
mutating func send(_ value: Value) {
Just(value).assign(to: &self)
}
}
Run Code Online (Sandbox Code Playgroud)