Работа с Swift 5 Exclusivectivity Enforcement при использовании Combine

В 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 быть struct к class, тогда исключение больше не выбрасывается.

Когда я рассматриваю, как я могу использовать Combine в существующей кодовой базе, а также в SwiftUIЯ вижу этот тип паттерна во многих местах. В старой модели делегата делегат довольно часто запрашивает отправляющий объект из обратного вызова делегата. В Swift 5 мне теперь нужно быть очень осторожным, чтобы ни один из этих обратных вызовов потенциально не считывался из свойства, которое инициировало уведомление.

Сталкивались ли с этим другие, и если да, то как вы к этому обратились? Apple обычно предлагает нам использовать structs где это имеет смысл, но, возможно, объект, который имеет опубликованные свойства, является одной из тех областей, где это не так?

1 ответ

password собственность не проблема. Это на самом деле proposal свойство. Если вы добавите didSet наблюдатель за недвижимостью proposal, вы увидите, что он сбрасывается, когда вы установите password, тогда вы получите доступ self?.proposal изнутри вашей раковины, пока она мутирует.

Я сомневаюсь, что это поведение, которое вы хотите, поэтому мне кажется, что правильное решение заключается в том, чтобы сделать PasswordProposal класс.

Другие вопросы по тегам