Нет никаких технических проблем с удалением наблюдателя из другого потока, но я бы не советовал этого делать. Наибольшую озабоченность вызывают условия гонки между освобождением наблюдателя и его отменой регистрации в KVO. Как , документация для removeObserver(_:forKeyPath:)
говорит:
Обязательно вызовите этот метод (или removeObserver(_:forKeyPath:context:)
) до того, как какой-либо объект, указанный в addObserver(_:forKeyPath:options:context:)
, будет освобожден.
Теперь в вашем случае оба ваших таймера строго ссылаются на наблюдателя (что само по себе является проблемой; вы должны использовать шаблон [weak self]
в своих таймерах и при необходимости аннулировать их), но если вы исправите это, вы теперь введете гонку.
Если вы добавите своих наблюдателей в init
и удалите их в deinit
, это устранит любые условия гонки. (Также, если вы используете современный синтаксис KVO , это также устраняет эту гонку за освобождение / отмену регистрации.)
Также помните, что observeValue(forKeyPath:of:change:context:)
вызывается в потоке, который обновил testValue
свойство. Поэтому, если вас беспокоят проблемы с потоками, следует также учитывать безопасность потоков как testValue
, так и counter
.
В отсутствие какой-либо синхронизации (например, возможно, вы собираетесь предположить, что testValue
никогда не будет обновляться из фонового потока, даже если вы планируете удалить наблюдателя из фонового потока), Я бы посоветовал сделать предположения, наследующие observeValue
явным образом. Итак, предполагая, что вы добавили своего наблюдателя следующим образом:
addObserver(self, forKeyPath: #keyPath(testValue), options: .new, context: &observerContext)
Затем вы можете добавить это dispatchPrecondition
, чтобы сделать предположение явным:
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey: Any]?, context: UnsafeMutableRawPointer?) {
guard context == &observerContext else {
super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
return
}
if keyPath == #keyPath(testValue) {
dispatchPrecondition(condition: .onQueue(.main)) // given that `counter` is not synchronized, let’s warn developer if ever updated `testValue` on background thread
print("counter: \(counter)")
counter += 1
}
}
Очевидно, вам это не нужно предварительное условие, если вы сделали counter
потокобезопасным с помощью некоторой синхронизации.
Кроме того, мы, очевидно, не будем использовать KVO для наблюдения за свойством текущего класса (обычно мы просто использовали бы Swift-наблюдатель ), но я предполагаю, что вы упростили это в иллюстративных целях.