1

There are several resources (blog, SO question, plus I've seen it used everywhere) that recommend removing an observer from the NotificationCenter in the deinit of the UIViewController, e.g.:

deinit {
    NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillShow, object: nil)
    NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}

Now while according to another blog entry I don't have to care about removing an observer from NotificationCenter since it uses weak references, I've seen the same pattern used with other references.

The question that bugs me. According to official documentation:

A deinitializer is called immediately before a class instance is deallocated. You write deinitializers with the deinit keyword, similar to how initializers are written with the init keyword. Deinitializers are only available on class types.

Doesn't this mean that if there still is a strong reference to the class, deinit will not get called, thus rendering the deinit references cleanup useless? If there is still a strong reference to the viewController from the NotificationCenter, then viewController's deinit will never get called, right? So removing the strong refenreces in deinit can never really work.

Am I missing something here?

Milan Nosáľ
  • 19,169
  • 4
  • 55
  • 90
  • 1
    Nosal If deinit is getting called that means you have no strong reference, but if not that means there is still a strong reference in code somewhere , which you will have to find and deallocate. – Tushar Sharma Dec 10 '17 at 18:14
  • As far as I remember, the notification center held a *unowned* ("__unsafe_unretained" in ARC lingo) to the observer in earlier iOS < 9, not a strong reference. That does not prevent deinit from being called. – As of iOS 9, it is a (zeroing) weak reference. – Martin R Dec 10 '17 at 18:28
  • Then what's the point in removing it in deinit? – Milan Nosáľ Dec 10 '17 at 18:30
  • Who says that it is still necessary in iOS >= 9? Compare https://useyourloaf.com/blog/unregistering-nsnotificationcenter-observers-in-ios-9/ or https://stackoverflow.com/questions/41689506/is-removing-observer-obligatory-necessary/41689713#41689713. – Martin R Dec 10 '17 at 18:35
  • OK, so it's around just for the backward compatibility, right? – Milan Nosáľ Dec 10 '17 at 18:57
  • From your own references: However when using the block based api addObserverForName a strong reference is still kept. I think that strong reference may also be unowned... Hence de need to still make the call – Caio Sym Dec 10 '17 at 20:29
  • 2
    @CaioSym - With block-based pattern, it's a strong reference if and only if (a) the closure references `self`; and (b) you neglect to use `[weak self]`/`[unowned self]` pattern. So, if you implement your closures properly, you do not need to remove observers in `deinit` (for iOS 9/macOS 10.11 and later). (And if you didn't implement your closures properly, `deinit` would never get called, anyway, rendering the removing of the observer there moot.) – Rob Dec 10 '17 at 20:48

1 Answers1

2

This statement

[...] that recommend removing an observer from the NotificationCenter in the deinit of the UIViewController [...]

was true in the past.

And your statement

[...] if there still is a strong reference to the class, deinit will not get called.

is correct.

Observers have weak reference

An observer holds a weak reference to the target object.

This explain why the deinit of an object will be called even if there are multiple active observers.

So why do we want to remove the observers in the deinit?

This was needed prior to iOS 9 to prevent an observer from invoking a method of a deallocated object.

However unregistering an observer is no longer needed from macOS 10.11 and iOS 9.0

In OS X 10.11 and iOS 9.0 NSNotificationCenter and NSDistributedNotificationCenter will no longer send notifications to registered observers that may be deallocated.

Source

Luca Angeletti
  • 58,465
  • 13
  • 121
  • 148