2

Swift 5,“对内存的独占访问”强制现在默认为发布版本启用,如 Swift.org 博客文章中所述:

Swift 5 排他性执法

我理解这个特性背后的原因,但是有了新的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从 a更改struct为 a class,则不再抛出异常。

当我考虑如何Combine在现有代码库以及SwiftUI. 在旧的委托模型中,委托从委托回调中查询发送对象是很常见的。在 Swift 5 中,我现在必须非常小心,这些回调没有一个可能从启动通知的属性中读取。

其他人是否遇到过这种情况,如果有,您是如何解决的?Apple 经常建议我们应该在structs有意义的地方使用,但也许具有已发布属性的对象是它没有的那些领域之一?

4

1 回答 1

1

The password property is not the problem. It's actually the proposal property. If you add a didSet property observer to proposal, you'll see it's getting reset when you set password, then you access self?.proposal from within your sink while it's being mutated.

I doubt this is the behavior that you want, so it seems to me like the correct solution is to make PasswordProposal a class.

于 2019-06-26T12:12:24.687 回答