When I use following Swift 4 closure-based KVO way, I got a crash on iOS 10 devices
class A: NSObject {
@objc dynamic var value: Int = 0
var observation: NSKeyValueObservation?
override init() {
super.init()
// Crash
self.observation = self.observe(\A.value, options: [.new], changeHandler: { (_, change) in
})
}
}
var a: A? = A()
a = nil
Crash info:
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'An instance 0x of class A was deallocated while key value observers were still registered with it. Current observation info: ( Context: 0x, Property: 0x>
I googled and got it was a Swift bug: https://bugs.swift.org/browse/SR-5816, and a workaround would be adding following lines inside deinit
deinit {
if #available(iOS 11.0, *) {
} else {
if let observation = self.observation {
self.removeObserver(observation, forKeyPath: "value")
}
}
}
My question is
Why following code wouldn't crash? (Just changing observing A to observing B, which is a member of A)
class A: NSObject {
@objc dynamic var value: Int = 0
var observation: NSKeyValueObservation?
let b = B()
override init() {
super.init()
self.observation = self.b.observe(\B.value, options: [.new], changeHandler: { (_, change) in
})
}
}
class B: NSObject {
@objc dynamic var value: Int = 0
}
var a: A? = A()
a = nil
In the meanwhile, if I add previous workaround to above code, I got a new crash, why?
deinit {
if let observation = self.observation {
self.b.removeObserver(observation, forKeyPath: "value")
}
}
New crash:
*** Terminating app due to uncaught exception 'NSRangeException', reason: 'Cannot remove an observer for the key path "value" from because it is not registered as an observer.'