Is it dangerous to directly pass object via [[NSNotificationCenter defaultCenter] postNotificationName] method? - ios

I am writting an SDK and looking for the best practise to send an object via NSNotification.
Apple and some threads including this indicate that the object param in postNotificationNamer should be the sender of the notification, which in most case, self. And your custom object should be passed via userInfo NSDictionary. From Apple doc:
Creates a notification with a given name, sender, and information and posts it to the receiver.
For example, the inappropriate practise is:
[[NSNotificationCenter defaultCenter] postNotificationName:#"myNotification"
object:myObject];
And the recommended way is:
NSDictionary* userInfo = #{#"myMessage": myObject};
[[NSNotificationCenter defaultCenter] postNotificationName:#"myNotification"
object:self
userInfo:userInfo];
I have tried both methods and they all work well. Question is, is there any risk to use former method? In my case I am not interested in the sender of the notification, but using the latter method introduces an additional wrapper (NSDicionary) around the actual object I want to send.

It's not dangerous to use the object parameter, but have in mind that NSNotificationCenter uses that parameter internally to decide some things. So if your code depends on things running in certain order or certain queues, it may behave unexpectedly.
Here's the relevant quote from the NSNotificationCenter Class Reference:
The object whose notifications you want to add the block to the operation queue.
If you pass nil, the notification center doesn’t use a notification’s sender to decide whether to add the block to the operation queue.
I would recommend that you used userInfo as intended, but it is not dangerous.

The latter is more flexible. While you may not care about the sender now, that could change. And you may later find you need to pass additional info.
Better to do it right upfront. It takes no extra effort to do it right the first time and it will be a lot of effort (and prone to mistakes) to have to go back and refactor a bunch of code when your needs change.
Plus, using the latter approach means your code is consistent with how it is done in other frameworks. Consistency makes code easier to read and maintain. You don't have to think about, "well in this case I get the object here, but in this case I get it there".

Related

Passing NSNotifications. Good? Bad? NBD?

I'm trying to think through the ramifications of passing an NSNotification object as a var to a method.
It seems like it's difficult to trace what happens to a the flow of logic with a notification anyway (without some great commenting and method/object naming) so passing the notification to another method/class/lib/project would just make it even less fun to debug or respond intelligently to an error.
But it just feels like it's the sort of thing that has a lot of hidden gotchas potentially.
And I know there are still a lot of people out there who love notifications.
So does anyone have some pros/cons/better-practices about passing the actual NSNotification object as a var between methods?
Just for clarity's sake here's some pseudo code:
[[NSNotificationCenter defaultCenter] postNotificationName:kSuperImportantNotification object:self userInfo:soMetaDict];
... Later in the code:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(importantThingHappened:) name:kSuperImportantNotification object:nil];
... Later in the code:
- (void) importantThingHappened:(NSNotification *)notification {
[respondingInterestedClass executeSomethingWith:notification];
}
... In "respondingInterestedClass":
- (void)executeServicesDiscoveredBlock:(NSNotification *)notification {
// ... do something with the NSNotificaiton object.
}
Thanks
It depends in what you are doing, usually this is when to use what:
One-to-One relationship and loose coupling: Delegate
One-to-One relationship and tight coupling: Direct method calls
One-to-Many relationship and loose coupling: Notifications
One-to-Many relationship and tight coupling: KVO
So you should not use Notifications for all kind of communication, lot of developers use them heavily even when its not needed.
Saying this, it is totally fine to pass the notification object to the method handler, but don't pass it anywhere else.
Putting aside how and when notifications should be used and focusing purely on the ephemeral set of uses that are totally the right choice....
Once received, the notification object is a state container that contains some set of state in a format that is convenient to the system. You can shoehorn whatever you want into it by shoving key/value pairs into a dictionary and passing it as the userInfo, if you want, but it is still the system state.
In a well designed body of code, there are hard fought barriers through which the format of the data and the encapsulated messaging are well defined. In a Model-View-Controller system, for example, a Model class won't be a subclass of UIView or handle user events directly.
And Notifications should be treated the same. When the notification is received, unwrap it into whatever internal-and-specific-to-your-architecture representation you want and pass that around.
Or, consider:
- (void) somethingChangedNotification:(NSNotification *)changed
{
[myModelGoop applyChange:...];
}
#implementation MyModelLayer
- (void)applyChange:(....)change
{
}
In the above, you could certainly pass the notification object into the model layer. But that's just pushing a system specific implementation detail into your model.
What happens the moment you have changes that aren't triggered via an NSNotification? Your either left to encapsulate your change into a fake NSNotification instance (because posting a notification may have side effects depending on who is observing) or you have to refactor the above to no longer take a notification in the model layer.
Better to get rid of the dependencies on system encapsulation early.
To that end, the NSNotification instance should never be passed out of the method that handles the notification.

Better way to pass userInfo data with NSNotificationCenter in iOS

[[NSNotificationCenter defaultCenter] postNotificationName:#"TapNewProduct" object:self.productID];
(or)
NSDictionary *dict = #{#"productID":self.productID};
[[NSNotificationCenter defaultCenter] postNotificationName:#"TapNewProduct" object:nil userInfo:dict];
Which is better method from above two?
Your first option abuses the 'sender' parameter of the notification because it's simple. It'll work, but it isn't correct. The idea with that parameter is that you can use it to filter the notifications that you receive. If you'll use it like that then fine, but it isn't for passing user info.
So, the second option is the correct one.
Imagine someone else coming to help on your project in the future - the more your code is written to follow standards the easier it'll be for them to help you.
In both the methods you are gonna get same output. you will fetch the object by notification.object.
but in this,
[[NSNotificationCenter defaultCenter] postNotificationName:#"TapNewProduct" object:self.productID];
there is no need to create a dictionary. it can reduce the code.
The object parameter is the "notificationSender", that is, the object posting the notification. The userInfo parameter is intended to contain information about the the notification and it may be nil.
More details in Apple's reference documentation.
postNotificationName:object: method invokes postNotificationName:object:userInfo: with a userInfo argument of nil.So basically there is no reason to argue with which one is better than the other.

KVO, properties changes and notifications

I have a shared singleton that contains all relevant information on the current user and its session, through the object [IWSession session] and [IWSession session].currentUser.
The current user (which actually refers to the one logged in the application) might have some of its properties updated frequently through webservice calls (triggered by iBeacon, triggered by a change in its location, etc).
This implies to update the GUI accordingly at different places in the app, let's say 5 or 6 class instances.
What's the proper way to update information displayed in the app as soon as any property is updated ?
I thought about
1) Adding a KVO on the [IWSession session].currentUser on himself and for all properties regarding the following link
Key Value Observing - how to observe all the properties of an object?
2) The KVO would then trigger a
[[NSNotificationCenter defaultCenter] postNotificationName:#"userUpdated" object:nil];
and all classes which need their layout to be updated would listen to that notification.
Is it a good approach ?
Any other suggestion ?
If you use Notifications then memory will not be released for all the objects in which notification is posted. Because if you use notifications then reference to the object is stored in the heap. So I don't this it would be a good idea to use Notifications.
I recently have been using a weak-observer list using hashtables
mObservers = [NSHashTable weakObjectsHashTable];
with semi-delegation messages, for example:
#protocol UserSessionObserver <NSObject>
- (void) userSession:(id)session didUpdateUser:(id)userProfile;
#end
So, any object that would be interested in changes to userProfile or userSession can simply add itself as an ad-hoc observer to the shared userSession. Because it is a weak entry, the object will be removed automatically from the observer table on dealloc.
Trick is to create the correct addObserver message:
- (void) addObserver:(__weak id<UserSessionObserver>)observer;
While this requires you to write your own observer logic, it also means:
1. No memory management issues with strong retain cycles,
2. Simplified and to the point observer messaging (the system notifications and KVO implementation is so generic, it is bordering on incomprehensible imo, but also adds several layers of logic that merely slows down processing).
An example implementation: Weak Observer example

How to retrieve all NSNotificationCenter observers?

I'd like to retrieve a list of observers (objects and selectors) for a given notification name. I know there's no official API for that. I also know I could subclass NSNotificationCenter to accomplish this. Sometimes however this is not a viable option because NSNotificationCenter usage is spread all over the code or even binary frameworks.
So I'm looking for an unofficial/private way to do this. (Since it's about debugging only, that's fine.)
Finally, Apple added a way to print all notification center observers:
po [NSNotificationCenter defaultCenter]
It prints a comma separated list with Name, Object, Observer, and Options:
<NSNotificationCenter:0x7f997b307500>
Name, Object, Observer, Options
WebPreferencesRemovedNotification, 0x11165b680, 0x116c87ff8, 1400
UIApplicationWillEnterForegroundNotification, 0x11165b680, 0x7f997a838000, 1400
...
If you don't want to subclass NSNotificationCenter you can rename original addObserver:selector:name:object method and create your own with such name and add observers in there to some array then call original renamed method.
Take a look at following methods: class_addMethod, class_replaceMethod, class_getMethodImplementation.
Also look at this SO question: Method Swizzling
I am not sure why you want observers but you might find this class useful, which removes observers automatically for you which I think might be what you want. SFObservers

Removing all notification observer from a single place

I want to remove a notification observer and I am using the method:
[[NSNotificationCenter defaultCenter] removeObserver: name:#"myNotification" object:nil];
for this. Now there are many observers who are listening to this notification and I want to remove all of them in one shot from a centralised place. Can I pass 'nil' in first parameter and it will remove all observers who are listening to myNotification?
You can remove an object from the notification center all together which means no notifications will get triggered. For example, when I have a view controller that has registered for notifications, I include this line in my dealloc.
[[NSNotificationCenter defaultCenter] removeObserver:self];
This is at the object level...so it will unregister for many notifications. It won't unregister for one notification in many objects.
Hope I understood your question correctly.
In case of Swift, you doing it like this:
NSNotificationCenter.defaultCenter().removeObserver(self)
And in Swift 3:
NotificationCenter.default.removeObserver(self)
Unfortunately, there is no way to remove all observers of a specific notification in one place. While there are certainly cases where this would be nice, it would be a dangerous thing to do as generally, the object doing the observing should be responsible for adding and removing itself as an observer of a particular notification. This ensures no unpredictable behavior b/c observers can come and go so they configure and clean up after themselves.
If an object that generates notifications goes away, it won't matter to the observer as the observer doesn't know about that object anyway. It just means that object won't be generating any more notifications.
[EDIT: RESPONSE TO YOUR COMMENT RE CLASS B STOPPING CLASS A FROM OBSERVING]
I just saw your comment. There are different ways to accomplish this, particularly if class B knows about class A. As you reference classes it sounds like you want to affect all instances of a class vs a particular instance. If you have some condition you can check when handling the notification, that's how I would approach this. In the notification handler something like:
if ([self shouldRespondToNotificationNamed:notification.name]) {
[self performNotificationAction];
}
If you don't have a condition you can check, then create one either in the class in question as an iVar or in a place where you can access it globally for all class instances. I generally use a singleton to store global app state that doesn't persist. If it persists, then use whatever method you're using for other state.

Resources