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,则会导致相同的异常。
但是,如果将PasswordProposalstruct更改为class,则不再引发异常。
当我考虑如何在现有的代码库中以及在Combine中使用SwiftUI时,我看到很多地方都会出现这种类型的模式。在旧的委托模型中,委托从委托回调中查询发送对象是很常见的。在Swift 5中,我现在必须非常小心,这些回调都不能从发起通知的属性中读取。
其他人有没有遇到过这个问题,如果有,你是怎么解决的?苹果公司经常建议我们应该在有意义的地方使用structs,但也许一个已经发布属性的对象是其中一个没有意义的领域?

最佳答案

password属性不是问题所在。它实际上是proposal属性。如果您将didSet属性观察器添加到proposal,当您设置password时,您将看到它正在重置,然后当它发生变化时,您可以从接收器内部访问self?.proposal
我怀疑这是您想要的行为,所以在我看来,正确的解决方案是将PasswordProposal设为类。

10-08 08:05