Работа с Swift 5 Exclusivectivity Enforcement при использовании Combine - PullRequest
1 голос
/ 25 июня 2019

Swift 5, принудительное применение «Эксклюзивного доступа к памяти» теперь включено по умолчанию для сборок релизов, как упомянуто в этом сообщении в блоге Swift.org:

Swift 5 Exclusiveibility 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 мне теперь нужно быть очень осторожным, чтобы ни один из этих обратных вызовов потенциально не считывался из свойства, которое инициировало уведомление.

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

1 Ответ

1 голос
/ 26 июня 2019

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

Iсомневаюсь, что это именно то поведение, которое вам нужно, поэтому мне кажется, что правильное решение - сделать PasswordProposal классом.

...