Reputation: 12431
I have an objective C class. In it, I created a init method and set up a NSNotification in it
//Set up NSNotification
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(getData)
name:@"Answer Submitted"
object:nil];
Where do I set the [[NSNotificationCenter defaultCenter] removeObserver:self]
in this class? I know that for a UIViewController
, I can add it into the viewDidUnload
method So what needs to be done if I just created an objective c Class?
Upvotes: 104
Views: 88080
Reputation: 8387
This is the only correct answer (all other are mistakenly suggest to use deinit
and dealloc
- this is clear misunderstanding of class instance lifecycle and iOS system).
Swift 5
There are two cases of using notifications:
viewWillAppear + viewWillDisappear
)viewDidLoad + viewWillDisappear
with "if").For the first case the correct place to add and remove observer are:
/// Add observers
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
NotificationCenter.default.addObserver(...)
}
/// Remove observers
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
// remove when screen dismissed
NotificationCenter.default.removeObserver(self)
}
for the second case the correct way is:
/// Add observers
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(...)
}
/// Remove observers
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
// remove only when view controller is removed disappear forever
if self.isBeingDismissed
|| !(self.navigationController?.viewControllers.contains(self) ?? true) {
NotificationCenter.default.removeObserver(self)
}
}
And never put removeObserver
in deinit{ ... }
, or dealloc
- it's a MISTAKE!
Upvotes: -1
Reputation: 38249
Note : This has been tested and working 100% percent
override func viewWillDisappear(animated: Bool){
super.viewWillDisappear(animated)
if self.navigationController!.viewControllers.contains(self) == false //any other hierarchy compare if it contains self or not
{
// the view has been removed from the navigation stack or hierarchy, back is probably the cause
// this will be slow with a large stack however.
NSNotificationCenter.defaultCenter().removeObserver(self)
}
}
PresentedViewController:
override func viewWillDisappear(animated: Bool){
super.viewWillDisappear(animated)
if self.isBeingDismissed() //presented view controller
{
// remove observer here
NSNotificationCenter.defaultCenter().removeObserver(self)
}
}
In iOS 6.0 > version
, its better to remove observer in viewWillDisappear
as viewDidUnload
method is deprecated.
[[NSNotificationCenter defaultCenter] removeObserver:observerObjectHere];
There is many times its better to remove observer
when the view has been removed from the navigation stack or hierarchy
.
- (void)viewWillDisappear:(BOOL)animated{
if (![[self.navigationController viewControllers] containsObject: self]) //any other hierarchy compare if it contains self or not
{
// the view has been removed from the navigation stack or hierarchy, back is probably the cause
// this will be slow with a large stack however.
[[NSNotificationCenter defaultCenter] removeObserver:observerObjectHere];
}
}
PresentedViewController:
- (void)viewWillDisappear:(BOOL)animated{
if ([self isBeingDismissed] == YES) ///presented view controller
{
// remove observer here
[[NSNotificationCenter defaultCenter] removeObserver:observerObjectHere];
}
}
Upvotes: 39
Reputation: 1163
override func viewDidLoad() { //add observer
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector:#selector(Yourclassname.method), name: NSNotification.Name(rawValue: "NotificationIdentifier"), object: nil)
}
override func viewWillDisappear(_ animated: Bool) { //remove observer
super.viewWillDisappear(true)
NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: "NotificationIdentifier"), object: nil)
}
Upvotes: 0
Reputation: 1004
It is important to notice also that viewWillDisappear
is called also when the view controller present a new UIView. This delegate simply indicate that the view controller main view is not visible on the display.
In this case, deallocating the notification in viewWillDisappear
may be inconvenient if we are using the notification to allow the UIview to communicate with the parent view controller.
As a solution I usually remove the observer in one of these two methods:
- (void)viewWillDisappear:(BOOL)animated {
NSLog(@"viewController will disappear");
if ([self isBeingDismissed]) {
NSLog(@"viewController is being dismissed");
[[NSNotificationCenter defaultCenter] removeObserver:self name:@"actionCompleted" object:nil];
}
}
-(void)dealloc {
NSLog(@"viewController is being deallocated");
[[NSNotificationCenter defaultCenter] removeObserver:self name:@"actionCompleted" object:nil];
}
For similar reasons, when I issue the notification the first time, I need to account for the fact that any time a view with appear above the controller then viewWillAppear
method is fired. This will in turn generate multiple copy of the same notification. Since there isn't a way to check if a notification is already active, I obviate the problem by removing the notification before adding it:
- (void)viewWillAppear:(BOOL)animated {
NSLog(@"viewController will appear");
// Add observers
[[NSNotificationCenter defaultCenter] removeObserver:self name:@"imageGenerated" object:nil]; // This is added to avoid duplicate notifications when the view is presented again
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(receivedImageFromCameraOrPhotolibraryMethodOnListener:) name:@"actionCompleted" object:nil];
}
Upvotes: 1
Reputation: 2339
Since iOS 9 it's no longer necessary to remove observers.
In OS X 10.11 and iOS 9.0 NSNotificationCenter and NSDistributedNotificationCenter will no longer send notifications to registered observers that may be deallocated.
Upvotes: 40
Reputation: 11390
If the observer is added to a view controller, I strongly recommend adding it in viewWillAppear
and removing it in viewWillDisappear
.
Upvotes: 25
Reputation: 1263
In my opinion, the following code makes no sense in ARC:
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
[super dealloc];
}
In iOS 6, there's also no sense in removing observers in viewDidUnload
, because it has been deprecated now.
To sum up, I always do it in viewDidDisappear
. However, it depends on your requirements also, just like @Dirk said.
Upvotes: 5
Reputation: 523
*edit: This advice applies to iOS <= 5 (even there you should be adding in viewWillAppear
and removing in viewWillDisappear
- however the advice applies if for some reason you've added the observer in viewDidLoad
)
If you've added the observer in viewDidLoad
you should remove it in both dealloc
and viewDidUnload
. Otherwise you'll end up adding it twice when viewDidLoad
is called after viewDidUnload
(this will happen after a memory warning). This isn't necessary in iOS 6 where viewDidUnload
is deprecated and won't be called (because views are no longer automatically unloaded).
Upvotes: 5
Reputation: 7806
In swift use deinit because dealloc is unavailable:
deinit {
...
}
Swift documentation:
A deinitializer is called immediately before a class instance is deallocated. You write deinitializers with the deinit keyword, similar to how intializers are written with the init keyword. Deinitializers are only available on class types.
Typically you don’t need to perform manual clean-up when your instances are deallocated. However, when you are working with your own resources, you might need to perform some additional clean-up yourself. For example, if you create a custom class to open a file and write some data to it, you might need to close the file before the class instance is deallocated.
Upvotes: 6
Reputation: 357
I think I found a reliable answer! I had to, as the answers above are ambiguous and seem contradicting. I looked through Cookbooks and Programming Guides.
First, the style of addObserver:
in viewWillAppear:
and removeObserver:
in viewWillDisappear:
does not work for me (I tested it) because I am posting a notification in a child view controller to execute code in the parent view controller. I would only use this style if I was posting and listening for the notification within the same view controller.
The answer I will rely on the most, I found in the iOS Programming: Big Nerd Ranch Guide 4th. I trust the BNR guys because they have iOS training centers and they are not just writing another cookbook. It is probably in their best interest to be accurate.
BNR example one: addObserver:
in init:
, removeObserver:
in dealloc:
BNR example two: addObserver:
in awakeFromNib:
, removeObserver:
in dealloc:
…when removing observer in dealloc:
they don’t use [super dealloc];
I hope this helps the next person…
I am updating this post because Apple now has almost completely gone with Storyboards so the above mentioned may not apply to all situations. The important thing (and the reason I added this post in the first place) is to pay attention if your viewWillDisappear:
is getting called. It wasn't for me when the application entered background.
Upvotes: 4
Reputation: 31053
The generic answer would be "as soon as you no longer need the notifications". This is obviously not a satisfying answer.
I'd recommend, that you add a call [notificationCenter removeObserver: self]
in method dealloc
of those classes, which you intend to use as observers, as it is the last chance to unregister an observer cleanly. This will, however, only protect you against crashes due to the notification center notifying dead objects. It cannot protect your code against receiving notifications, when your objects are not yet/no longer in a state in which they can properly handle the notification. For this... See above.
Edit (since the answer seems to draw more comments than I would have thought) All I am trying to say here is: it's really hard to give general advice as to when it's best to remove the observer from the notification center, because that depends:
So, the best general advice I can come up with: to protect your app. against at least one possible failure, do the removeObserver:
dance in dealloc
, since that's the last point (in the object's life), where you can do that cleanly. What this does not mean is: "just defer the removal until dealloc
is called, and everything will be fine". Instead, remove the observer as soon as the object is no longer ready (or required) to receive notifications. That is the exact right moment. Unfortunately, not knowing the answers to any of the questions mentioned above, I cannot even guess, when that moment would be.
You can always safely removeObserver:
an object multiple times (and all but the very first call with a given observer will be nops). So: think about doing it (again) in dealloc
just to be sure, but first and foremost: do it at the appropriate moment (which is determined by your use case).
Upvotes: 113
Reputation: 8661
The accepted answer is not safe and could cause a memory leak. Please do leave the unregister in dealloc but also deregister in viewWillDisappear (that is of course if you register in viewWillAppear)....THAT'S WHAT I DID ANYWAYS AND IT WORKS GREAT! :)
Upvotes: 2
Reputation: 12335
-(void) dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
[super dealloc];
}
Upvotes: 20