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
там, где это имеет смысл, но, возможно, объект, который имеет опубликованные свойства, является одной из тех областей, где это не так?