In Swift 4, how do I remove a block-based KVO observer?
In iOS 11, you don't have to. Just let the observer go out of scope. There is no penalty any longer for letting an observer die before the observed or for letting the observed die before the observer, so you have no actual work to do.
On the other hand, if you really want to unregister the observer, remove it from whatever is retaining it, or tell it to invalidate
. (Something must be retaining it, because if you don't persist the observer, it will die and your observer function will never be called.)
(You say "if I store an observer like this", but the way you are storing it, with let
, is a somewhat silly way to store the observer. It would be better to put it in a Set from which you can remove it later, or at least store it in a Optional var
that you can later set to nil
.)
How to remove an KVO observer added with observe() API?
Example of using key-value observing
/// define an Observer
var observation: NSKeyValueObservation?
override func viewDidLoad() {
super.viewDidLoad()
/// start observation
observation = view.observe(\.backgroundColor, options: [.old, .new], changeHandler: { (view, value) in
})
/// invalidate observation
observation?.invalidate()
observation = nil
}
Do observers need to be removed in Swift 4/Objective-C?
If you are talking about NSKeyValueObservation
: No they don't.
From the transcript of the WWDC 2017 Video "What is new in Foundation"
There is no need for a
deinit
where I throw away or tear down my observation because it's tied to the life cycle of that observation token. And so when the controller goes away, the observation token will go away.
Best practice to remove an object as observer for some KVO property
In broad terms, you need to unregister for the KVO observation when you don't need it anymore. To prevent the error you're seeing, you need to be sure you've unregistered before the observed object is deallocated.
In practical terms, that means that you really shouldn't observe something if you don't have any control over its lifecycle ie. you don't have a strong reference to it. If you do have a strong reference to it, you need to unregister before your strong reference goes away. Typically the way I handle this is to handle unregisteration on an old value and registration on the new value in a custom setter for a (strong) property referring to the object to be observed. Then, in dealloc, I also unregister my observance. Something like this:
- (void)setSomeView:(NSView *)someView
{
if (someView != _someView) {
[_someView removeObserver:self forKeyPath:@"someKey"];
_someView = someView;
[_someView addObserver:self forKeyPath:@"someKey" options:0 context:NULL];
}
}
- (void)dealloc
{
[_someView removeObserver:self forKeyPath:@"someKey"];
}
That way, I only observe objects that I have a strong (owning) reference to, so they can't be deallocated out from under me. And, when I'm deallocated, I also unregister for the observation.
Why do I get a crash clearing block-based KVO on self with NSManagedObject?
The key issue seems to be that my Core Data model is in a module.
I have updated the sample project to show the underlying bug here.
I have spoken with Apple support. It seems there is a system bug. Swift does not currently correctly clean up an NSKeyValueObservation on an NSManagedObject when it is in a module.
I have submitted an issue to Apple through Feedback assistant.
Swift NotificationCenter remove observer quickest way
@Sh_Khan is right:
NotificationCenter.default.removeObserver(self)
You can get even further, as mentioned in the Apple Documentation:
If your app targets iOS 9.0 and later or macOS 10.11 and later, you don't need to unregister an observer in its dealloc method.
Swift 4 Using KVO to listen to volume changes
I assume you're referring to the line:
You can use Key-value observing (KVO) to observe state changes to many of the player’s dynamic properties...
This use of "dynamic" isn't the same thing as Objective-C's @dynamic
or Swift's dynamic
. The docs just mean "properties that change" in this context, and they're telling you that the AVPlayer is generally very KVO-compliant and intended to be observed that way. "KVO compliant" means it follows the change notification rules. There are many ways to achieve that, both automatic and manual. The docs are just promising that AVPlayer does.
(An important point about Cocoa that distinguishes it from many other systems is that Cocoa handles many things "by convention". There's no way to say in code "this is KVO compliant" and there is no way for the compiler to enforce it, but Cocoa developers tend to be very good about following the rules. When ARC was developed, it relied heavily on the fact that Cocoa developers had for years named methods following very specific rules that indicate how memory management is handled. It just added complier enforcement of the rules Cocoa developers had always followed by hand. This is why Cocoa developers get very noisy about naming conventions and capitalization. There are major parts of Cocoa that rely entirely on following consistent naming rules.)
Remembering that the AVPlayer interface is an Objective-C API that happens to be bridged to Swift, there's no equivalent of the Swift keyword dynamic
in that case. That's a keyword that tells Swift that this property may be observed and so its accessors can't be optimized to static dispatch. That's not something Objective-C requires (or can do; all ObjC properties are "dynamic" in this sense).
The Objective-C @dynamic
is a completely different thing, only weakly related to KVO (though it comes up in a lot of KVO-heavy contexts like Core Data). It just means "even though you can't find an accessor implementation for this property anywhere, trust me, by the time this runs an implementation will be available." This relies on the ability of ObjC's runtime to generate implementations dynamically or dispatch in programmer-controlled ways (this still kind of exists in Swift by manipulating the ObjC runtime, but it isn't really a "Swift" feature).
As for how KVO works, it's one of the few true "magic tricks" in Cocoa. For a quick intro, see Key-Value Observing Implementation Details. The short version is:
- When you observe an object, a subclass for that object is dynamically created (yes, a new class is invented at runtime).
- The subclass adds calls to
willChangeValue...
anddidChangeValue...
around all calls to the superclass's property accessors. - The object is "ISA-swizzled" to be that new class.
- Magic! (Ok, not really magic; it's just code, but it's quite a trick.)
EDIT: The original question never mentioned that it wasn't working. The reason it's not working is because you're not assigning the returned NSKeyValueObservation
in a property; you're just throwing it away. I'm surprised there's not a warning about that; I may open a radar.
When the returned NSKeyValueObservation
deallocates, the observation goes away, so this creates an observation and immediately destroys it. You need to store it in a property until you want the observation to go away.
Related Topics
Remove or Edit User Location Blue Pulsing Circle
How to Fix Cocoapod .Modulemap File Not Found
Why Is an Observedobject Array Not Updated in My Swiftui Application
How to Convert Double to Int in Swift
Using a Dispatch_Once Singleton Model in Swift
Get Nth Character of a String in Swift Programming Language
Round Trip Swift Number Types To/From Data
How to Convert Data to Hex String in Swift
Shall We Always Use [Unowned Self] Inside Closure in Swift
Why Create "Implicitly Unwrapped Optionals", Since That Implies You Know There's a Value
Instance Member Cannot Be Used on Type
How to Convert a View (Not Uiview) to an Image
Swift 2 - Pattern Matching in "If"
Closure With Generic Parameters
Arkit - What Do the Different Columns in Transform Matrix Represent
What Is the Point of Having Two Different Names For the Same Parameter