Slow response using NSNotification defaultCenter - ios

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()

Related

NSNotification Observer added in viewWillAppear is executed twice in iOS 13

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?

The NSNotificationCenter is never executed

This is my code.
Here create the observer to Notification called Example into ViewController
- (void)addObserverExample
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(example:)
name:#"Example"
object:nil];
}
- (void)example:(NSNotification *)notification{
NSLog(#"Example!!!");
}
From viewDidLoad register my observer
- (void)viewDidLoad
{
[self addObserverExample];
}
In my second ViewController. When tapped a button excute this code:
[[NSNotificationCenter defaultCenter] postNotificationName:#"Example" object:self.dictKeys userInfo:nil];
The problem I have is that the notification is never executed.
Any idea.
Have created demo for NSNotificationCenter as per your question and it's working fine for me. Here it is the link of that code: NSNotificationCenter Demo
- (void)viewDidLoad {
[super viewDidLoad];
[self addObserverExample];
}
- (void)addObserverExample
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(example:)
name:#"Example"
object:nil];
}
- (void)example:(NSNotification *)notification{
NSLog(#"Example!!!");
NSLog(#"%#",notification.userInfo);
}
- (IBAction)btnFireNotification:(id)sender {
[[NSNotificationCenter defaultCenter] postNotificationName:#"Example" object:nil userInfo:#{#"key" : #"value"}];
}
I believe the problem you're having may be related to the fact that in your second view controller, you're passing self.dictKeys in the object parameter.
If you want to pass data via the NSNotificationCenter, you should use the userInfo parameter instead.
Darshan's example does this the correct way.

Notification fired twice even tho adding observer only once

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.

Issue while calling NSNotificationCenter

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..

How to refresh View after entering applicationWillEnterForeground? [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How to tell when controller has resumed from background?
How to refresh View after user enters applicationWillEnterForeground?
I want to complete recall for example HomeViewController.
I have and update function in HomeViewController and I want when user enters to call update function and reload table data.
Any class can register to UIApplicationWillEnterForegroundNotification, and react accordingly. It's not reserved to the app delegate, and helps for better separation of source code.
Create a viewDidLoad method like this for your HomeViewController
- (void)viewDidLoad
{
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(yourUpdateMethodGoesHere:)
name:UIApplicationWillEnterForegroundNotification
object:nil];
}
// Don't forget to remove the observer in your dealloc method.
// Otherwise it will stay retained by the [NSNotificationCenter defaultCenter]...
- (void) dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
[super dealloc];
}
If your ViewController is a tableViewController your could also directly call the reload data function:
- (void)viewDidLoad
{
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:[self tableView]
selector:#selector(reloadData)
name:UIApplicationWillEnterForegroundNotification
object:nil];
}
- (void) dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
[super dealloc];
}
Or you could use a block:
[[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationWillEnterForegroundNotification
object:nil
queue:[NSOperationQueue mainQueue]
usingBlock:^(NSNotification *note) {
[[self tableView] reloadData];
}];
You could declare a property in your app delegate class that would point to the HomeViewController object. Then you can call your update function in applicationWillEnterForeground.

Resources