我正在try 创建一个类似于Combine
的Published
的属性包装器(用于我的项目需要),但能够通过向发布者发送存储在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