Normally I add my observers in viewWillAppear and remove them in viewWillDisappear. In this case I need one of the observers to continue even after the view is gone so that it can finish some work. In order to make sure that the observer is only added once with this view, I do the following:
-(void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:YES];
[[NSNotificationCenter defaultCenter]removeObserver:self
name:#"imageSaved"
object:nil];
[[NSNotificationCenter defaultCenter]addObserver:self
selector:#selector(postMessageWithImage:)
name:#"imageSaved"
object:nil];
}
I have performed a search through the rest of the application to ensure that this observer is NOT registered anywhere else. Unfortunately sometimes, but not all times and there is no consistent factor, the notification is fired twice. I have also ensured with breakpoints and NSLog that the postNotifcationName is NOT called more than once. I have not been able to reproduce on the iPhone as the problem seems confined to the iPad.
In further troubleshooting I have checked that the method is being called from the same thread (no reason it wouldn't be but just to check). This problem DOES go away if I put the removeObserverin viewWillDisappear, however, again that is not how I need this to work.
Clearly this is a case where the observer for this is being registered twice but I cannot find a reason why that would be. As you can see from the code, any time this observer is registered it is first removed. My only other thought is whether self could get "corrupted" such that the removeObserverwouldn't function properly?
Add your observer when the view will show, and remove it when will disappear.
ADD:
- (void)viewDidLoad
{
[[NSNotificationCenter defaultCenter]addObserver:self
selector:#selector(postMessageWithImage:)
name:#"imageSaved"
object:nil];
}
REMOVE:
- (void)postMessageWithImage:(NSNotification*)aNotification
{
[[NSNotificationCenter defaultCenter]removeObserver:self
name:#"imageSaved"
object:nil];
// here do your job
}
This is perfectly valid and efficient.
Instead of adding the observer in viewWillAppear:, try adding the observer you wish to persist when the view disappears in viewDidLoad. Then you can call your removeObserver:name:object: in your dealloc method
If you just want something to be executed once, put it in the predicate of a dispatch_once() call, like
static dispatch_once_t lock;
dispatch_once(&lock, ^{
// put your addObserver call here
});
Related
I dont want to add observer in viewDidAppear and remove in viewDidDisappear.Will not serve my case.
I have tried doing it in dealloc.
My root VC is in navController.Then a second VC is pushed in navController, where I addObserver for notifications to be sent from rootVC.The problem is when I pop the secondVC its dealloc is not called immediately or may be somtimes not called alltogether.
- (void)viewDidLoad {
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(newMessagesNotification:) name:_newMessageNotificationListenerName object:nil];
}
- (void)newMessagesNotification:(NSNotification *)notification {
//some implementation
}
If you do not want to remove in the ViewDidDisapear than i think you should remove it right after you called the navigationCotnroller pop methode. But I think we can't tell you the exact moment when you should remove because we don't know when you want to remove it and why it isn't good in the ViewWillDisapier or in the ViewDidDisapier.
I want to call a method from another class via NSNotificationCenter.Everything is working fine.
The problem is my method called up two times.
ViewController.m
- (void)viewDidLoad {
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(removeAllSubViews:) name:#"getTheRequest" object:nil];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)removeAllSubViews:(NSNotification *)notification
{
NSLog(#"%#",notification.object);
NSLog(#"Print");
}
ViewController2.m
- (void)viewDidLoad {
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] postNotificationName:#"getTheRequest" object:#"mySite"];
// Do any additional setup after loading the view.
}
When I run, I get this in console:
Why my method is called up two times ?
Edit
When I use this code in ViewController2.m it works fine. But Why?? **
[[NSNotificationCenter defaultCenter] removeObserver:self name:#"getTheRequest" object:nil];
You have to remove observer after use of it inside the method.
- (void)removeAllSubViews:(NSNotification *)notification
{
[[NSNotificationCenter defaultCenter] removeObserver("getTheRequest")]
NSLog(#"%#",notification.object);
NSLog(#"Print");
}
I've seen this before when I had a retain cycle in my view controller, so every time a instance of this view controller was created, it was added as an NSNotificationCenter observer, but because of the retain cycle when the view controller was dismissed, it was never actually deallocated/released from memory, so technically it was still an observer.
You might want to try to add the following to your view controller:
- (void)dealloc {
NSLog(#"Dealloc called.");
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
This should remove your view controller as an observer when it is dismissed, if there is no retain cycle, and if there is the NSLog will never be called - which is indicative of a larger memory-related issue, where what you're seeing is just a side-effect.
You are probably seeing multiple calls to that method, because you register the observer multiple times (e.g. you have navigated to that view controller before), but did not remove it again at the appropriate places. Anyways, viewDidLoad is very likely not the ideal place to register an observer. A common place to do this is the designated initializer, and removing it again in dealloc.
As a side note (and without seeing enough code for a very informed opinion), your use case ("remove all subviews") does not sound like notifications are a good approach. Have you considered using delegation?
It is possible that there is a double NSNotificationCenter registration.
I think you have declared another:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(removeAllSubViews:) name:#"getTheRequest" object:nil];
somewhere.. trying finding the other one..
And just a tip, when you register for NSNoticationCenter try removing first the observer like:
// removes the observer
[[NSNotificationCenter defaultCenter] removeObserver:YourObserver name:YourName object:YourObject];
followed by:
// register
[[NSNotificationCenter defaultCenter] addObserver:YourObserver selector:YourSelector name:YourName object:YourObject];
Just to remove the existing one, if any..
I am fairly new to iOS dev. I have been trying to find an answer to this but don't have a definite solution..
I have a view called FirstView.m
And there's AppDelegate.m
A background task is running in AppDelegate.m which updates a variable called `text' depending upon the closest beacon to the phone.
When the app is in FirstView, I want to update a UILabel inside FirstView as per the variable text of AppDelegate.
I know this can be done by running a background thread in FirstView which every 1 second checks whether the variable in AppDelegate was changed or not, but this doesn't seem efficient to me at all, there is no point running two background threads for the same task.
My questions is, is there a way to update the label from AppDelegate itself ? Something on the lines of performSelectorOnMainThread ?
Thanks!
You could post a notification with the text in the userInfo dictionary in the AppDelegate anytime that value changes:
text = [iBeacon updateText]; // just a random method name I made up
[[NSNotificationCenter defaultCenter] postNotificationName:#"textChanged"
object:self
userInfo:#{#"text":text}];
Then in the FirstView you can listen for that notification to update the UI (normally in viewDidLoad or in viewDidAppear:). Be sure to unregister before the view controller is dealloced somewhere in FirstView so there are no memory leaks. Example for registering and updating UI
You can register like this:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(handleTextChanged:)
name:#"textChanged"
object:nil];
Then update the UI:
- (void)handleTextChanged:(NSNotification *)notification
{
// Be sure to update the UI on the main thread
dispatch_async(dispatch_get_main_queue(), ^{
self.label.text = notification.userInfo[#"text"];
});
}
And finally, unregister for notifications:
[[NSNotificationCenter defaultCenter] removeObserver:self];
One more way you can try the same using keyValue Observing. Refer this Apple Documentation and how to implement the same.
Within an App I make use of several viewcontrollers. On one viewcontroller an observer is initialized as follows:
[[NSNotificationCenter defaultCenter] removeObserver:self name:#"MyNotification" object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(myMethod:) name:#"MyNotification" object:nil];
Even when removing the NSNotification before initializing the number of executions of myMethod: is being summed up by the amount of repeated views on the respective viewcontroller.
Why does this happen and how can I avoid myMethod: being called more then once.
Note: I made sure by using breakpoints that I did not made mistakes on calling postNotification multiple times.
Edit: This is how my postNotification looks like
NSArray * objects = [NSArray arrayWithObjects:[NSNumber numberWithInt:number],someText, nil];
NSArray * keys = [NSArray arrayWithObjects:#"Number",#"Text", nil];
NSDictionary * userInfo = [NSDictionary dictionaryWithObjects:objects forKeys:keys];
[[NSNotificationCenter defaultCenter] postNotificationName:#"myNotification" object:self userInfo:userInfo];
edit: even after moving my subscribing to viewwillappear: I get the same result. myMethod: is called multiple times. (number of times i reload the viewcontroller).
-(void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[[NSNotificationCenter defaultCenter] removeObserver:self name:#"MyNotification" object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(myMethod:) name:#"MyNotification" object:nil];
}
edit: something seems wrong with my lifecycle. ViewDidUnload and dealloc are not getting called, however viewdiddisappear is getting called.
The way I push my Viewcontroller to the stack is as follows where parent is a tableview subclass (on clicking the row this viewcontroller is initiated:
detailScreen * screen = [[detailScreen alloc] initWithContentID:ID andFullContentArray:fullContentIndex andParent:parent];
[self.navigationController pushViewController:screen animated:YES];
Solution:
Moving removal of nsnotification to viewdiddisappear did the trick. Thanks for guidance!
Based on this description, a likely cause is that your viewcontrollers are over-retained and not released when you think they are. This is quite common even with ARC if things are over-retained. So, you think that you have only one instance of a given viewcontroller active, whereas you actually have several live instances, and they all listen to the notifications.
If I was in this situation, I would put a breakpoint in the viewcontroller’s dealloc method and make sure it is deallocated correctly, if that’s the intended design of your app.
In which methods did you register the observers?
Apple recommends that observers should be registered in viewWillAppear: and unregistered in viewWillDissapear:
Are you sure that you don't register the observer twice?
Ran into this issue in an application running swift. The application got the notification once when first launched. the notification increases the number of times you go into the background and come back. i.e
app launches one - add observer gets gets called once in view will appear or view did load - notification is called once
app goes into background and comes back, add observer gets called again in view will appear or view did load. notification gets called twice.
the number increases the number of times you go into background and come back.
code in view will disappear will make no difference as the view is still in the window stack and has not been removed from it.
solution:
observe application will resign active in your view controller:
NSNotificationCenter.defaultCenter().addObserver(self, selector: "applicationWillResign:", name: UIApplicationWillResignActiveNotification, object: nil)
func applicationWillResign(notification : NSNotification) {
NSNotificationCenter.defaultCenter().removeObserver(self)
}
this will make sure that your view controller will remove the observer for the notification when the view goes into background.
it is quite possible you are subscribing to the notifications
[[NSNotificationCenter defaultCenter] postNotificationName:#"myNotification" object:self userInfo:userInfo];
before self gets initialized. And trying to unsubscribe 'self' which isn't really subscribed to, and you will get all global myNotification notifications.
If your view was hooked up in IB, use -awakeFromNib: as the starting point to register for notifications
It is possible that the class with the observer is, quite appropriately, instantiated multiple times. When you are debugging it will kinda look like the notification is being posted multiple times. But if you inspect self you might see that each time is for a different instance.
This could easily be the case if your app uses a tab bar and the observer is in a base class of which your view controllers are subclasses.
I have added NSNotificationCenter in viewDidLoad method and removed in viewDidUnload but it's not getting removed. I am following ARC. I have followed few answer but I didn't get luck. I dont have reputation for give comments so posting some thing looks like duplicate. Please don't -ve votes.
Sample code:
- (void)viewDidLoad
{
[[NSNotificationCenter defaultCenter ] addObserver:self.containerView
selector:#selector(loadInitialScreen)
name:CLEARSCREEN_DEPOSIT
object:NULL];
}
- (void)viewDidUnload
{
[[NSNotificationCenter defaultCenter] removeObserver:self.containerView
name:CLEARSCREEN_DEPOSIT
object:nil];
}
You should remove the observer either in -viewWillDisappear:, -viewDidDisappear: or in the -dealloc method, depending on your needs. The reason is -viewDidUnload in iOS6+ is never called anymore and before iOS6 it's called when a memory warning is received.
Try to use viewDidDisappear instead viewDidUnload :
-(void)viewDidDisappear:(BOOL)animated
{
[[NSNotificationCenter defaultCenter] removeObserver:self.containerView
name:CLEARSCREEN_DEPOSIT object:nil];
[super viewDidDisappear:animated];
}
viewDidUnload is called (for < iOS 6.0), when a memory warning is received to the application/view controller.
It will not be called for removal of the view, for that dealloc is called. But as you are using ARC, you cannot implement dealloc method.
The best bet is to remove the observer in the method loadInitialScreen, if it has to be called only once.
If your notification can be posted multiple times, it's better to remove the observer in viewDidDisappear, but then add observer for the notification in ViewWillAppear