When to unsubscribe from a NSNotification in a UIView
Asked Answered
A

7

16

I am using the following NSNotifications within a UIView so that the view can be notified when a UIKeyboard appears and adjust its position (frame) on screen:

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardDidShow:) name:UIKeyboardDidShowNotification object:nil];

The two notifications above are being subscribed to within the -init method of the UIView. Where is the best place to unsubscribe from these notifications once the view has disappeared off-screen? At the moment the app is crashing whenever the UIKeyboard appears in another view, presumably because a notification is still being sent to the then released UIView.

Also, is there a better place to be subscribing to the notifications, apart from within the -init method?

Thanks for any assistance.

Agrostology answered 20/11, 2011 at 10:1 Comment(1)
Thanks for the good question, helped me in a different use-case where the custom UIView has to respond changing its subviews appearance based on a control value of one of its subviews.Weatherbound
J
41

-[UIView willMoveToWindow:] and -[UIView didMoveToWindow] are called even when a view is removed from a window. The window argument (or the window property in the case of -didMoveToWindow) will be nil in that case, i. e.:

- (void)willMoveToWindow:(UIWindow *)newWindow {
    if (newWindow == nil) {
        // Will be removed from window, similar to -viewDidUnload.
        // Unsubscribe from any notifications here.
    }
}

- (void)didMoveToWindow {
    if (self.window) {
        // Added to a window, similar to -viewDidLoad.
        // Subscribe to notifications here.
    }
}

Except for a few edge cases this is a safe way to do it. If you need more control, you can observe the hidden property of the window to which your view belong.

Jarv answered 20/11, 2011 at 17:34 Comment(4)
I like this method as it suits my use-case much better. Can anyone see any downsides? I am also removing the observer in the -dealloc method as a fail safe.Agrostology
I've been looking for this for agesLyse
"Except for a few edge cases this is a safe way to do it." I'm starting to rely on this behavior in several locations, so I'd be very curious to know what those few edge cases are.Publicness
I’m not quite sure what edge cases I had in mind. Maybe the window being deallocated wouldn’t trigger this?Jarv
A
4

I put my removeObserver: calls in -dealloc.

Haven't had any problems so far.

Antecedence answered 20/11, 2011 at 10:13 Comment(3)
Thanks, that's what I was thinking but wasn't sure whether it was the most suitable place - although if you haven't had any problems then it might be the best.Agrostology
My thinking is dealloc is the last thing that should be called on an object, so it's best to put it there.Antecedence
Sometimes, dealloc is called seconds later. In this case, if you have notification post, you will be in some troubleBrogdon
S
2

The definitive answer (e.g. ensure that an object is no longer reference by NSNotificationCenter when its lifecycle ends) is to do as @Tom suggests and remove itself as an observer in dealloc.

The subjective answer is it is also good practice to stop observing whenever the notifications are no longer relevant to the object. This is completely up to you to decide based on the design of your app. For example, if you have views that stay alive but come in and out of view you may decide to start observing when they are added to a subview and stop observing when they are removed.

WRT where the notification logic should reside (in a view vs in a controller), that's also up to you, obviously it can work both ways. I would make the decision based on the circumstances. If handling the notification in the view requires pushing app logic into the view (i.e. treating the view like a controller) then that's a red flag.

Skewer answered 20/11, 2011 at 17:50 Comment(1)
Thanks for the great answer. Your elaboration helped a lot. Because I'm using my custom UIView inside reusable UITableViewCell, I opted-in for subscribing in didMoveToWindow and unsubscribing in willMoveToWindow. I'm using NSNotificationCenter to enable my custom UIView class to observe a value of a subview control, so the said UIView class would react reflecting visual feedback changes on other subviews.Weatherbound
C
1

First, you should consider when you want to stop receiving notifications:

  1. When view is deallocated
  2. When view is disappeared

You should always check if your view observes notifications and call -removeObserver: in -dealloc. In addition if you consider 2, override -viewWillDisappear or -viewDidDisappear or any other point where you manipulate view hierarchy of view's UIViewController.

I recommend you to put the logic into UIViewController because in terms of relationships UIView doesn't own its frame.

Cristiano answered 20/11, 2011 at 10:24 Comment(0)
K
0

You can create a seperate function for adding and removing observes and then later you can all those functions from the view's instance. By the way for your question's answer I would have remove the observers before removing the view itself from superview. I hope you understand.

Koniology answered 20/11, 2011 at 10:13 Comment(1)
Thanks for your reply - I was hopping for a neater way than manually having to call those methods from the superview (i.e. view controller class) so I am thinking that the -dealloc method mentioned by Tom might be the best.Agrostology
O
-1

To unsubscribe you can use

- (void)removeObserver:(id)notificationObserver

or

- (void)removeObserver:(id)notificationObserver name:(NSString *)notificationName object:(id)notificationSender

Both methods are NSNotificationCenter's instance methods.

Take a look at NSNotificationCenter Class Reference

Opinion answered 20/11, 2011 at 10:7 Comment(1)
Thanks, but I am already aware of the methods to unsubscribe. I am mainly interested in the best-practice placement of these methods within a UIView class.Agrostology
W
-2

As a practice for keyboard notifications, I normally use

addObserver

in

viewWillAppear

and

removeObserver

in

viewWillDisAppear

works great for me everytime and it ensures that no keyboard notification is passed to a view which is not on screen which keeps the app safe from crashing due to false keyboard notofications.

Wilie answered 20/11, 2011 at 10:14 Comment(4)
Thanks, but those are only methods in a UIViewController. I am purely using a UIView.Agrostology
no issues. you can create a custom class extending UIView and then override viewWillAppear and viewWillDisappear and make this implementation. Then use this class instead or Native UIview..Wilie
as for keeping remove observer in dealloc is little more unsafe than keeping it in viewWillDisappearWilie
@SaurabhPassolia sorry that's plain wrong. FWIW for whoever is reading, the above suggestions in the comments motivates encapsulating UIView within a UIViewController just for the sake of adding/removing observer within viewWillAppear and viewWillDisappear is plain out wrong. Please check out the accepted answer by gcbrueckmann's answer, as well as XJones's answer for elaboration on the accepted answer. Saurabh Passolia's answer works but for UIViewController only, but the above suggestions for UIView in the comments is plain wrong in my opinion.Weatherbound

© 2022 - 2024 — McMap. All rights reserved.