Swift 5,“独占内存访问”强制现在默认启用,用于发布版本,如Swift.org博客中所述:
Swift 5 Exclusivity Enforcement
我理解这个特性背后的原因,但是对于新的Combine
框架,我觉得一些非常正常的设计模式现在要崩溃了,我很好奇如何最好地解决它们。
对于Combine
来说,代码的某些部分对模型中的更改做出反应是很自然的,这样它们可能需要从模型刚刚更改的属性中读取数据。但它们不能再这样做了,因为当您试图读取当前设置的值时,它将触发内存异常。
请考虑以下示例:
struct PasswordProposal {
let passwordPublisher = CurrentValueSubject<String, Never>("1234")
let confirmPasswordPublisher = CurrentValueSubject<String, Never>("1234")
var password:String {
get { passwordPublisher.value }
set { passwordPublisher.value = newValue }
}
var confirmPassword:String {
get { confirmPasswordPublisher.value }
set { confirmPasswordPublisher.value = newValue }
}
var isPasswordValid:Bool {
password == confirmPassword && !password.isEmpty
}
}
class Coordinator {
var proposal:PasswordProposal
var subscription:Cancellable?
init() {
self.proposal = PasswordProposal()
self.subscription = self.proposal.passwordPublisher.sink { [weak self] _ in
print(self?.proposal.isPasswordValid ?? "")
}
}
// Simulate changing the password to trigger the publisher.
func changePassword() {
proposal.password = "7890"
}
}
// --------------------------------
var vc = Coordinator()
vc.changePassword()
一旦调用
changePassword()
,互斥强制将抛出一个异常,因为属性password
将在当前写入时尝试读取。请注意,如果将此示例更改为使用单独的备份存储属性而不是
CurrentValueSubject
,则会导致相同的异常。但是,如果将
PasswordProposal
从struct
更改为class
,则不再引发异常。当我考虑如何在现有的代码库中以及在
Combine
中使用SwiftUI
时,我看到很多地方都会出现这种类型的模式。在旧的委托模型中,委托从委托回调中查询发送对象是很常见的。在Swift 5中,我现在必须非常小心,这些回调都不能从发起通知的属性中读取。其他人有没有遇到过这个问题,如果有,你是怎么解决的?苹果公司经常建议我们应该在有意义的地方使用
structs
,但也许一个已经发布属性的对象是其中一个没有意义的领域? 最佳答案
password
属性不是问题所在。它实际上是proposal
属性。如果您将didSet
属性观察器添加到proposal
,当您设置password
时,您将看到它正在重置,然后当它发生变化时,您可以从接收器内部访问self?.proposal
。
我怀疑这是您想要的行为,所以在我看来,正确的解决方案是将PasswordProposal
设为类。