In Swift 4, How to Remove a Block-Based Kvo Observer

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... and didChangeValue... 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



Leave a reply



Submit