I came across a strange problem when handling a NSNotification iOS 13
// Adding the observer in viewWillAppear
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
...
NSLog(#"Adding observer");
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(handleNotification) name:#"someNotification" object:nil];
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
...
[[NSNotificationCenter defaultCenter] removeObserver:self name:#"someNotification" object:nil];
NSLog(#"Removed observer");
}
- (void)handleNotification {
NSLog(#"Handle notification");
}
I have double checked that the notification is posted only once. There is only one place in the app where someNotification is posted and a breakpoint and NSLog confirms, that this code is only executed once.
Result in iOS 12 and below:
Adding observer
Handle notification
Result in iOS 13 and below:
Adding observer
Handle notification
Handle notification
So in iOS 13 the observer is called twice while the exact same code in iOS 12 and blow is only called once.
My first guess was, that for some reason in iOS 13 viewWillAppear is called twice that thus the observer is added twice. However, in this case the NSLog output Adding Observer should also appear twice, shouldn't it?
BUT: The problem can be solved by removing the observer in viewWillAppear before adding it. This makes sure, that the observer is added only once:
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
...
NSLog(#"Adding observer");
[[NSNotificationCenter defaultCenter] removeObserver:self name:#"someNotification" object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(handleNotification) name:#"someNotification" object:nil];
}
So, since this solves the problem the observer obviously is added twice in viewWillAppear. But why?
Why is viewWillAppear called twice in iOS 13? And even more important: How is it possible, that it is called twice but the NSLog output Adding Observer appears only once?
Related
I have two class which uses NSNotification to communicate with each other.
Currently, i have an issue with notification being fired twice, i've double/triple/even more checked that observer is not added more then 1 time, notification not being posted twice, did global search on my project for same notification.
My code is like below
Added Notification Observer
[[NSNotificationCenter defaultCenter] removeObserver:self name:notification_deleteMediaFromGallery object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(notificationReceiver:) name:notification_deleteMediaFromGallery object:nil];
Notification Receiver
- (void)notificationReceiver:(NSNotification*)notification {
if ([notification.name isEqualToString:notification_deleteMediaFromGallery]) {
if ([[notification.userInfo objectForKey:#"kind"] integerValue]==GalleryKindPhoto) {
//My statements
}
else if ([[notification.userInfo objectForKey:#"kind"] integerValue]==GalleryKindVideo) {
//My statements
}
}
}
Post Notification
dispatch_async(dispatch_get_main_queue(), ^{
[_browser reloadData];
[[NSNotificationCenter defaultCenter] postNotificationName:notification_deleteMediaFromGallery object:nil userInfo:#{#"index":#(_browser.currentIndex), #"kind":#(self.kind), #"function":[NSString stringWithFormat:#"%s",__PRETTY_FUNCTION__]}];
});
I have also tried this solution by EmptyStack but not get it to work.
I'll be very thankful to you if you could help me solve this issue.
Thanks.
Edit
NOTE
I've added observer in my viewdidload, and cant add/remove observer from viewwillappera/viewwillappear or viewdidappear/viewdiddisappear because the next viewcontroller which will be pushed on current viewcontroller will post notifications
I think you need to write dealloc method in your view controller. And remove All Notification observer in dealloc method,
- (void)dealloc
{
// Deregister observer
[[NSNotificationCenter defaultCenter] removeObserver:self name:notification_deleteMediaFromGallery object:nil];
}
Hi please make sure your method is not calling two time from where you are firing notification.
& please add your notification observer in viewWillDisappear method.
Hi i am trying to use NSNotification center in my application.The selector method is not being called from the code.I found similar questions in this site like this, but still i am unable to resolve the error.
i am posting notification in appdelegate did finish launching as:
[[NSNotificationCenter defaultCenter] postNotificationName:#"ualert" object:self userInfo:userDict];
adding an observer in one of the view controller as:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(remoteNotificationReceived:)
name:#"ualert"
object:nil];
my selector method is:
- (void)remoteNotificationReceived:(NSNotification *)notification
{
NSLog(#"Notification: %#", notification.userInfo);
}
removing observer as:
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
You are posting ualert in applicationDidFinishLaunching which will necessarily occur before your view controller is loaded (and therefore before you have added the observer for the notification).
I have a UITabBarController with some tab and in all of them i have:
-(void)viewDidappear:(BOOL)animated{
......
[[NSNotificationCenter defaultCenter] addObserverForName:kNotificationName object:nil queue: nil, usingBlock{...}
}
and
-(void)viewDidDisappear:(BOOL)animated{
......
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
In another class i post the notification with name kNotificationName:
[[NSNotificationCenter defaultCenter] postNotification:kNotificationName object:nil];
I've set some log on all of this methods, and the order they are called is correct but...if i switch from First to Second tab (and the notification is posted), the first and the second tab receive the notification (but the viewDidDisappear of the first tab is called!).
If from second tab i go to third tab, the first, second, and third tab receive the notification.
I've tried to use:
[[NSNotificationCenter defaultCenter] removeObserver:self name:postNotification:kNotificationName object:nil];
but the behaviour is the same. All observer are notified.
EDIT1:
As suggest to the other topic i've moved all in viewWillAppear: and viewWillDisappear:, but this haven't have any effect.
I've tried to remove the observer after have received the notification, like this:
-(void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[[NSNotificationCenter defaultCenter] addObserverForName:kDLSyncEngineSyncCompletedNotificationName object:nil queue:nil usingBlock:^(NSNotification *note) {
....
[[NSNotificationCenter defaultCenter] removeObserver:self name:kDLSyncEngineSyncCompletedNotificationName object:nil];
}];
}
But, using this (bad) approach too, the notification is received to the first tab too (i've added this code only on the first tab, to check if after press the second tab, the first tab is receiving again the notification).
EDIT2 - SOLVED - (but what is the difference?)
Instead of using *addObserverForName:object:queue:usingBlock*: I've used *addObserver:selector:name:object:* and in this way all works.
Apple documentation say this for the method with usingBlock:
The block is copied by the notification center and (the copy) held
until the observer registration is removed.
Sure, I've called removeObserver....
Inside the block in the addObserver method -
usingBlock:^(NSNotification *notification){
/*
do something
*/
[[NSNotificationCenter defaultCenter] removeObserver:self];
}];
As you might have already checked this in documentation -
Be sure to invoke removeObserver: or removeObserver:name:object: before notificationObserver or any object specified in addObserver:selector:name:object: is deallocated.
I'm developing an iOS app with latest SDK.
It's a fullscreen app.
I have a method on viewWillAppear method that has to be called every time the apps comes from background.
- (void) viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self setUpVideo];
}
On setUpVideo I set up AVCaptureVideoPreviewLayer because I lose the video when the apps come back from background.
As I have read, viewWillAppear isn't called when the apps come back from background and now, I don't know where to put that code.
On this question, occulus suggest to use [[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(doMyLayoutStuff:) name:UIApplicationDidChangeStatusBarFrameNotification object:nil]; but it doesn't work for me.
- (void)viewDidLoad
{
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(setUpVideo:) name:UIApplicationDidChangeStatusBarFrameNotification object:nil];
}
Any advice?
Observe UIApplicationWillEnterForegroundNotification instead.
- (void)viewDidAppear {
[super viewDidAppear];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(enterForeground:)
name:UIApplicationWillEnterForegroundNotification
object:nil];
// ...
}
- (void)enterForeground:(NSNotification *)notification {
// do stuff
}
Don't call viewWillAppear: directly from the enterForeground: method. Instead move all required code to a separate method and call that from both viewWillAppear: and enterForeground:.
applicationWillEnterForeground will trigger when app comes from background
- (void)applicationWillEnterForeground:(UIApplication *)application
{
// Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
}
Additionally, you can use UIApplicationDidBecomeActiveNotification for firing some method
[[NSNotificationCenter defaultCenter] addObserver: self
selector: #selector(handleMethod:)
name: UIApplicationDidBecomeActiveNotification
object: [UIApplication sharedApplication]];
Try posting this notification from
- (void)applicationDidBecomeActive:(UIApplication *)application of AppDelegate(or observe corresponding notification which is better)
Hi I am using NSNotificationCenter defaultCenter to implement the 'like' and 'comment' functions in my app.
//In Answer Table View
#implementation AnswerTableView
- (id)initWithParentController:(UIViewController *)pController andResourcePath:(NSString *)thisResourcePath {
....
// Notification to reload table when a comment is submitted
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(reloadTable)
name:#"Comment Submitted"
object:nil];
// Notification to reload table when an answer is liked
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(reloadTable)
name:#"Answer Liked"
object:nil];
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
[super dealloc];
}
//In custom button implementation - THIS BUTTON IS CREATED IN EVERY CELL OF THE TABLEVIEW
#implementation UICustomButton
-(id)initWithButtonType:(NSString *)type {
self = [super init];
if (self) {
//Initialization done here
}
return self;
}
- (void)buttonPressed {
if ([btnType isEqualToString:#"like"]) {
[[NSNotificationCenter defaultCenter] postNotificationName:#"Answer Liked" object:nil];
}
else if ([btnType isEqualToString:#"comment"]) {
[[NSNotificationCenter defaultCenter] postNotificationName:#"Comment Submitted" object:nil];
}
}
However, I realize that after using these functions for a while, the response speed of the table reload gets slower and slower (to a point where it crashes).
Did I miss out anything in the implementation i.e. deallocating etc
You are repeatedly adding observers and the slowdown occurs because the notification code has to cycle over more and more observers to send notifications. You are probably crashing because you are leaking so many of these views.
Put a log statement in your dealloc to see if these instances are ever cleaned up. Also there can be timing issues with removeObserver in a dealloc method. try to remove the observer before dealloc if you can.
Sometimes its good to queue the event with Grand Central Dispatch to make sure its running on the main thread.
dispatch_async(dispatch_get_main_queue()