Você pode usar o KVO no Swift, mas apenas para dynamic
propriedades da NSObject
subclasse. Considere que você queria observar a bar
propriedade de uma Foo
classe. No Swift 4, especifique bar
como dynamic
propriedade em sua NSObject
subclasse:
class Foo: NSObject {
@objc dynamic var bar = 0
}
Você pode se registrar para observar alterações na bar
propriedade. No Swift 4 e no Swift 3.2, isso foi bastante simplificado, conforme descrito em Usando a observação de valores-chave no Swift :
class MyObject {
private var token: NSKeyValueObservation
var objectToObserve = Foo()
init() {
token = objectToObserve.observe(\.bar) { [weak self] object, change in // the `[weak self]` is to avoid strong reference cycle; obviously, if you don't reference `self` in the closure, then `[weak self]` is not needed
print("bar property is now \(object.bar)")
}
}
}
Observe que, no Swift 4, agora temos uma digitação forte de caminhos-chave usando o caractere barra invertida (o \.bar
é o caminho-chave para a bar
propriedade do objeto que está sendo observado). Além disso, como ele está usando o padrão de fechamento de conclusão, não precisamos remover manualmente os observadores (quando o token
escopo está fora do escopo, o observador é removido para nós) nem precisamos nos preocupar em chamar a super
implementação se a chave não Combine. O fechamento é chamado apenas quando esse observador específico é chamado. Para mais informações, consulte o vídeo da WWDC 2017, What's New in Foundation .
No Swift 3, para observar isso, é um pouco mais complicado, mas muito semelhante ao que se faz no Objective-C. Ou seja, você implementaria observeValue(forKeyPath keyPath:, of object:, change:, context:)
qual (a) garante que estamos lidando com o nosso contexto (e não com algo que nossa super
instância registrou para observar); e então (b) manuseie ou repasse para a super
implementação, conforme necessário. E certifique-se de se remover como observador, quando apropriado. Por exemplo, você pode remover o observador quando ele é desalocado:
No Swift 3:
class MyObject: NSObject {
private var observerContext = 0
var objectToObserve = Foo()
override init() {
super.init()
objectToObserve.addObserver(self, forKeyPath: #keyPath(Foo.bar), options: [.new, .old], context: &observerContext)
}
deinit {
objectToObserve.removeObserver(self, forKeyPath: #keyPath(Foo.bar), context: &observerContext)
}
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
}
// do something upon notification of the observed object
print("\(keyPath): \(change?[.newKey])")
}
}
Observe que você só pode observar propriedades que podem ser representadas em Objective-C. Assim, você não pode observar genéricos, struct
tipos Swift enum
, tipos Swift , etc.
Para uma discussão sobre a implementação do Swift 2, veja minha resposta original, abaixo.
O uso da dynamic
palavra-chave para atingir o KVO com NSObject
subclasses é descrito na seção Observação de valor-chave do capítulo Adotando convenções de design de cacau do guia Usando Swift com cacau e Objective-C :
A observação do valor-chave é um mecanismo que permite que os objetos sejam notificados sobre alterações nas propriedades especificadas de outros objetos. Você pode usar a observação de valores-chave com uma classe Swift, desde que a classe seja herdada da NSObject
classe. Você pode usar estas três etapas para implementar a observação de valores-chave no Swift.
Adicione o dynamic
modificador a qualquer propriedade que você deseja observar. Para mais informações dynamic
, consulte Exigir expedição dinâmica .
class MyObjectToObserve: NSObject {
dynamic var myDate = NSDate()
func updateDate() {
myDate = NSDate()
}
}
Crie uma variável de contexto global.
private var myContext = 0
Adicione um observador para o caminho da chave, substitua o observeValueForKeyPath:ofObject:change:context:
método e remova o observador deinit
.
class MyObserver: NSObject {
var objectToObserve = MyObjectToObserve()
override init() {
super.init()
objectToObserve.addObserver(self, forKeyPath: "myDate", options: .New, context: &myContext)
}
override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
if context == &myContext {
if let newValue = change?[NSKeyValueChangeNewKey] {
print("Date changed: \(newValue)")
}
} else {
super.observeValueForKeyPath(keyPath, ofObject: object, change: change, context: context)
}
}
deinit {
objectToObserve.removeObserver(self, forKeyPath: "myDate", context: &myContext)
}
}
[Observe que essa discussão sobre o KVO foi removida posteriormente do guia Usando Swift com cacau e Objective-C , que foi adaptado para o Swift 3, mas ainda funciona conforme descrito na parte superior desta resposta.]
Vale a pena notar que o Swift possui seu próprio sistema de observação de propriedades nativas , mas isso é para uma classe que especifica seu próprio código que será executado mediante a observação de suas próprias propriedades. O KVO, por outro lado, foi projetado para registrar-se para observar alterações em algumas propriedades dinâmicas de alguma outra classe.