In Swift 4, how do I remove a block-based KVO observer?

13,860

Solution 1

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.)

Solution 2

With Swift 5, I began using .observe(\.propertyName, ...) on core data objects as the tokens automatically unregister at deinit or an invalidate() call on the token.

This works remarkably well until I recently noticed that I was leaking objects. I was seeing leaked NSKeyValueObservance, NSKeyValueObservationInfo, and NSArray objects. After verifying that I was managing the tokens properly, I finally tracked down the problem.

If you perform an .observe() on a Core Data object, you must keep the object as well as the token. If the object turns into a fault before you invalidate/release the token, you will leak memory. You do not crash but once it turns into a fault you will leak memory even if you free the token.

Share:
13,860
Guilherme
Author by

Guilherme

:D

Updated on June 30, 2022

Comments

  • Guilherme
    Guilherme almost 2 years

    If I store an observer like this:

    let observer: NSKeyValueObservation = foo.observe(\.value, options: [.new]) { (foo, change) in
        print(change.newValue)
    }
    

    How do I remove/disable/cleanup observer once I no longer need it?

    My foo instance does not have any remove-like method that receives an NSKeyValueObservation instance, the observer itself doesn't have any remove-like either.

  • Guilherme
    Guilherme over 6 years
    Then I suppose I didn't really need to store it in a variable, even though Xcode will raise a Result of call to 'observe(_:options:changeHandler:)' is unused warning. Is that correct?
  • matt
    matt over 6 years
    You do need to store it, for the reason I gave in the second paragraph.
  • Guilherme
    Guilherme over 6 years
    Rephrasing my question: if I don't need to manually invalidate it, and let it die "naturally", I won't need to store it in a variable. Correct?
  • matt
    matt over 6 years
    Incorrect, for the reason I gave in the second paragraph.
  • Miles Egan
    Miles Egan over 6 years
    I just had a crash in one of my apps caused by a dangling observation after releasing an AVPlayerItem it was observing. So in some cases you really do need to call invalidate() yourself.
  • matt
    matt over 6 years
    @MilesEgan In iOS 11?
  • fespinozacast
    fespinozacast over 6 years
    one thing to point out is to hold a reference to the observation, otherwise it will be deallocated at the end of the function
  • fespinozacast
    fespinozacast over 6 years
    also, it's an iOS 11 only feature or Xcode 9 / Swift 3.2-Swift 4.0 thing? as a project i am working on supports iOS 10 too
  • Kartick Vaddadi
    Kartick Vaddadi over 6 years
    Your statement that we don't need to — is it only for the new block-based KVO, or also for old-style KVO on iOS 11?
  • matt
    matt over 6 years
    @VaddadiKartick See developer.apple.com/library/content/releasenotes/Foundation/‌​… and esp. under "Relaxed Key-Value Observing Unregistration Requirements"
  • Kartick Vaddadi
    Kartick Vaddadi over 6 years
    Thanks. Is that only on macOS, as that documentation says?
  • matt
    matt over 6 years
    @VaddadiKartick Have you considered trying it? It's very easy to test this. Crashing is obvious when it happens — and when it doesn't.
  • Kartick Vaddadi
    Kartick Vaddadi over 6 years
    Crashes may also be intermittent, or happen in some code paths, which we didn't think of testing. I wouldn't do things the documentation says not to do. In this case, I'd unregister all observers. Thanks for your help.
  • mfaani
    mfaani almost 6 years
    Just to be crystal clear 1. It would be better to put it in a Set as in a collection? Because you may have multiple items to observe? Interesting way. 2. I read your comments and 2nd paragraph. You can still persist it by an normal property and then once the object is deallocated then your observation is undone without doing anything. Right?
  • matt
    matt almost 6 years
    @Honey can you think of a better way than a Set? You wouldn't want to declare multiple named instance properties just to hold each of the observers! — Yes, nothing more to do in either case; the view controller goes out of existence, the observers go out of existence, they unregister themselves as they go, and all's well that ends well. — Note that that's only for KVO observers. NSNotificationCenter observers work quite differently.
  • mfaani
    mfaani almost 6 years
    Thanks. Since iOS9 I thought 'NSNotificationCenter observers' go out of memory as soon the object is deallocated. What you're saying is against this. I don't want its answer given here. Just that do you have a previous answer written for what you just said? Can you share its link? or I should open a new question?
  • matt
    matt almost 6 years
    @Honey I'm not saying anything about NSNotificationCenter observers. This question is about KVO observers and that's all I've talked about.
  • Manuel
    Manuel over 4 years
    @matt You mentioned "Relaxed Key-Value Observing Unregistration Requirements" -- am I reading the docs correctly, that the object has to have a method automaticallyNotifiesObserversForKey to indicate that it supports auto-removing of the KVO? In my case it's AVPlayerItem which does not seem to have such a method with deployment target iOS11.1.
  • matt
    matt over 4 years
    @Manuel I don't know what you mean by that. AVPlayerItem returns true from automaticallyNotifiesObserversForKey for every key I tried, including in iOS 11. Can you show your code?
  • Manuel
    Manuel over 4 years
    @matt you are right, I should have been more precise; the instantiated object did not have the method, but the class does have the static method automaticallyNotifiesObserversForKey, thanks for checking.
  • matt
    matt over 4 years
    @Manuel Yes, the "Relaxed ..." doc is clear about that, this is a class method. Of course to see that, you have to be able to read Objective-C notation. :) Every class has this method; it is inherited from the base class, NSObject. See developer.apple.com/documentation/objectivec/nsobject/… The only question of interest is whether the class returns true or false for a given key. But a return value of false from a built-in KVO-compliant class is so unlikely that the probability approaches zero; no need to check, really.
  • Manuel
    Manuel over 4 years
    I overlooked that, my eyes got too used to the Swift elegance.