In my iOS application, I am posting a NSNotification and catching it in one of my UIView in main thread. I want to pass extra information along with the notification. I was using userInfo dictionary of NSNotification for that.
[[NSNotificationCenter defaultCenter] postNotificationName:#"NotifyValueComputedFromJS" object:self userInfo:#{#"notificationKey":key,#"notificationValue":value,#"notificationColor":color,#"notificationTimeStamp":time}];
key, value, color and time are local variables which contains the value I need to pass. In UIView I am adding observer for this notification and I am using notification.userInfo to get these data
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(receiveNotification:) name:#"NotifyValueComputedFromJS" object:nil];
-(void)receiveNotification:(NSNotification *)notification
{
if ([notification.userInfo valueForKey:#"notificationKey"]!=nil && [[notification.userInfo valueForKey:#"notificationKey"] isEqualToString:self.notificationKey] && [notification.userInfo valueForKey:#"notificationValue"]!=nil) {
[self updateLabelWithValue:[notification.userInfo valueForKey:#"notificationValue"]];
}
}
The frequency in which this notification is posted is 4 times in one second. I am doing some animations also in main thread. The problem I am facing here is my UI is lagging. UI will respond to scroll events or touch events with huge delay(I have faced a delay of even 1 to 2 seconds). After some research I came to know that NSDictionary is bulky and will cause lag if used in main thread. Is there any other way I can pass my data through NSNotification?
I have tried out another way. I have created a custom NSObject class to save the data I want and I am passing it as the object parameter of postNotification method.
[[NSNotificationCenter defaultCenter] postNotificationName:#"NotifyValueComputedFromJS" object:customDataObject userInfo:nil];
Here customDataObject is an instance of my custom NSObject class. I know the parameter is meant to be the sender of notification(usually it will be self). Is it a wrong approach if I am sending a custom object as parameter?
As BobDave mentioned, the key is to send the notification on some thread other than the main UI thread. This can be accomplished with dispatch_async, or with a queue.
The typical pattern for this behavior is sender:
-(void)sendDataToObserver {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:#"NotifyValueComputedFromJS" object:customDataObject userInfo:userInfo:#{#"notificationKey":key,#"notificationValue":value,#"notificationColor":color,#"notificationTimeStamp":time}];
});
}
And receiver (NOTE: weak self because retain cycles):
-(void)addObserver {
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(receiveNotification:) name:#"NotifyValueComputedFromJS" object:nil];
}
-(void)receiveNotification:(NSNotification *)notification {
if ([notification.userInfo valueForKey:#"notificationKey"]!=nil && [[notification.userInfo valueForKey:#"notificationKey"] isEqualToString:self.notificationKey] && [notification.userInfo valueForKey:#"notificationValue"]!=nil) {
__weak typeof (self) weakSelf = self;
dispatch_async(dispatch_get_main_queue(), ^{
[weakSelf updateLabelWithValue:[notification.userInfo valueForKey:#"notificationValue"]];
});
}
}
Maybe you could use - addObserverForName:object:queue:usingBlock:
and use a non-main queue to execute the block in order to reduce the lag. Also, shouldn't the observer be added in a UIViewController, not a UIView?
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.
I have this method to get shift my text fields when they keyboard appears:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
- (void)keyboardWillShow:(NSNotification *)notification {
self.scrollView.contentOffset = CGPointMake(0.0f, keyboardShift);
}
Then I tried to use the block method instead:
[[NSNotificationCenter defaultCenter] addObserverForName:UIKeyboardWillShowNotification object:nil queue:nil usingBlock:^(NSNotification *note) {
self.scrollView.contentOffset = CGPointMake(0.0f, keyboardShift);
}];
However, using this method I set a breakpoint inside the block but it's not being called. Is there anything I'm missing? Why doesn't this method work but the other one does?
What if you try replacing the nil parameter for the queue with [NSOperationQueue mainQueue]
Also, if you go with the block API, make sure you keep a reference to the token returned so that you can remove the observer at the right time later on.
addObserverForName returns an object that you should hold on to. From the header:
The return value is retained by the system, and should be held onto by the caller in order to remove the observer with removeObserver: later, to stop observation.
See the example in the documentation.
I have the following code within AppDelegate. The purpose being to create a couple of observers, and then call some code. Once that code completes it then posts a notification, and the observer should then remove both observers and call the completion handler.
My issue is that it appears that the observers are not being removed as I expected. The notification is posted, and the NSLog entry is written to console, so I know that the observer is working. However, on the second time of calling, the NSLog is called twice, third time three times etc.
My thoughts are that this is to do with the removal being within the block of code that is running from the observer, however, I am unsure how I can resolve this (if indeed this is what the issue is).
Could someone be so kind as to explain how I can achieve this?
Thanks.
-(void) application:(UIApplication *)application performFetchWithCompletionHandler:
(void (^)(UIBackgroundFetchResult))completionHandler {
[[NSNotificationCenter defaultCenter] addObserverForName:#"fetchDidCompleteNewData" object:nil
queue:nil usingBlock:^(NSNotification *completed) {
//Remove Observers
[[NSNotificationCenter defaultCenter] removeObserver:self
name:#"fetchDidCompleteNewData"
object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self
name:#"fetchDidCompleteNoData"
object:nil];
// Post completion
completionHandler(UIBackgroundFetchResultNewData);
NSLog(#"Background fetch completed... New Data");
}];
[[NSNotificationCenter defaultCenter] addObserverForName:#"fetchDidCompleteNoData" object:nil
queue:nil usingBlock:^(NSNotification *completed) {
//Remove Observers
[[NSNotificationCenter defaultCenter] removeObserver:self
name:#"fetchDidCompleteNoData"
object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self
name:#"fetchDidCompleteNewData"
object:nil];
//post completion
completionHandler(UIBackgroundFetchResultNoData);
NSLog(#"Background fetch completed... No New Data");
}];
GetDetails *getDetails = [[GetDetails alloc] init];
[getDetails backgroundRefresh];
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[[UIApplication sharedApplication] setMinimumBackgroundFetchInterval:UIApplicationBackgroundFetchIntervalMinimum];
return YES;
}
You are not registering self as an object. Moreover, when the block is pushed onto the stack by addObserverForName: the method has not yet returned so the notification is nil.
Make a global object using block, eg
__block __weak id notification;
then,
notification = [[NSNotificationCenter defaultCenter] addObserverForName:#"fetchDidCompleteNewData" object:nil queue:nil usingBlock:^(NSNotification *completed) {
//Remove Observers
[[NSNotificationCenter defaultCenter] removeObserver:notification];
}];
My thoughts are that this is to do with the removal being within the
block of code that is running from the observer, however, I am unsure
how I can resolve this (if indeed this is what the issue is).
Could someone be so kind as to explain how I can achieve this?
Certainly.
You can easily test your theory by not using the addObserverForName:object:queue:usingBlock: method and instead using the addObserver:selector:name:object: method, where the selector is the name of a function you call instead of using a block.
Simply use the API guide for NSNotificationCenter for details about these methods, or in general, since you question was about what other method you could use that does not require a block statement, consulting the API is the first place to check for alternative tools within the class.
I'm developing an app which has to communicate with an external accessory. The app has several requests to send to the external accessory.
My problem:
I'm using observers in different places (classes), I'm adding the following observers in viewDidLoad:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(observer1:)
name:EADSessionDataReceivedNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(observer2:)
name:EADSessionDataReceivedNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(observer3:)
name:EADSessionDataReceivedNotification object:nil];
The 1st Observer works very well, but I'm getting problems with the other two. They don't respond until the first one has been used. Do I need to add something else?
The flow is as follows:
Send a request to ext-acc and fire a flag to know which observer will take the returned data
ext-acc responds with data
The receiver method pushes a notification into notification center.
The observer with the flag in 1 will take the data (at this point do I need to remove the notification since no one else will need it?).
Looks you have a misunderstanding regarding how NSNotificationCenter works. You are registering your object (self) to observe the notification EADSessionDataReceivedNotification three times, each with it's own selector (observer1, observer2, observer3).
So, what is happening is correct for your code as written. When EADSessionDataReceivedNotification is posted, NSNotificationCenter sends the specified selector to each observer. There is no conditional logic or way to cancel a notification.
Given your description, it sounds like you should only be observing the notification once and checking your flag to determine how to process. Something like:
// observe notificaton
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(dataReceived:) object:nil];
// notification handler
- (void)dataReceived:(NSNotification *)notification {
if (someCondition) {
[self handleDataCondition1];
}
else if (aSecondCondition) {
[self handleDataCondition2];
}
else if (aThirdCondition) {
[self handleDataCondition3];
}
else {
// ????
}
}
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()