So far I have been using NSNotificationCenter with the method postNotification:aString object:anyObjectOfInterestForTheReceiver. But recently I read in the documentation that the object field should only be passed self. Is there any terrible side effect I am unaware of that should convince me to only pass self in the future, or is it OK to pass any object?
Thank you!
You can pass any object as the object of a notification, but the convention is that the object is the "thing that is doing the notifying" (and you put other relevant state in the userInfo dictionary).
The reason why it's mostly self is because usually the object doing the notifying usually wants to reference itself. That way, for example, if you had many Foo objects, and one of them completed a task and sent a notification, anyone observing the notification could just look at object to see which Foo was the one in question. The observer can also choose to observe only notifications from a particular Foo when you follow this scheme.
It's also reasonable (though less common) to use something besides "self" when posting a notification-- let's say you're sending a notification "on behalf of" another object. For example, you could be a singleton controller object that completes a Bar task, and you could send the notification with a reference to the particular Bar as the object. That makes more sense than using the singleton as the object, since there'd be no interesting variance there.
Again, this is a (useful) convention only. When you make up your own notification, you get to define the "contract" of the notification, which is the name, what kind of object is used as the object, and what's in the userInfo.
Yes, there is a side effect I can think of. Let me explain it.
The method you talk about was actually defined in NSNotification.h as below:
(void)postNotificationName:(NSString *)notificationName object:(id)notificationSender
The first parameter notificationName stands for the name of the notification.
The second parameter notificationSender stands for the object posting the notification.
Yes, any object can be a notification sender, even nil can be.
In terms of observering the notification(to became a observer of a specific notification), we got to know another method defined in NSNotification:
(void)addObserver:(id)notificationObserver selector:(SEL)notificationSelector name:(NSString *)notificationName object:(id)notificationSender
As you can see, the last parameter is notificationSender(the object whose notifications the observer wants to receive).
So right now, the side effect is apparently. Let me explain it in detail. There are three controllers A, B, C.for example. The controller A post a notification helloEveryone. The controller B also post a notification helloEveryone.
In C controller, if you place a statement like below:
[[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(helloEveryOne) name:#"helloEveryone" object:nil]
Then you will receive two helloEveryone from controller A and controller B.
if you place a statement like this:
[[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(helloEveryOne) name:#"helloEveryone" object:controllerA]
Then you will receive only one helloEveryone from controller A.
The self surely is not a nil notification sender as long as it can post a notification, but use another object as a notification sender, may be it is a nil object. And the observer's behave is different in terms of that whether notification sender is nil or not.
It is crucial if the events really have sender.
When you register an observer, you can specify the sender that you want to observe. If you are letting other people using your message, so it is crucial to pass sender correctly or the observer that bound to specific sender will not get your message.
Related
I have a class (call it classA) that contains a property named info (a model class, containing lots of info), wich in turn contains a property named name (a string). I want another class (classB) to receive a KVO notification when the string name changes in classA.
This is what I'm doing now on classB:
[classA addObserver: self forKeyPath: #"info.name" options: 0 context: nil];
There are two ways the value name changes on classA: when it is set directly like classA.info.name = ... and when info is set like classA.info = ...
When name is changed directly KVO works perfectly. However, when the info property is set and name changes indirectly, I get this error:
Cannot update for observer <classB> for the key path "info.name" from <classA>, most likely because the value for the key "info" has changed without an appropriate KVO notification being sent. Check the KVO-compliance of the classA class.
What should I change on classA to make this work?
The cause of this issue comes from the fact that you are implementing the setter -setInfo: for info and calling willChangeValueForKey: | didChangeValueForKey: inside it.
I've seen many instances in which it is believed that the will|didChange... calls need to be implemented for the KVO notification to be fired. This is true only when the setter is not explicitly called. When you do call the setter, the KVO mechanism takes care of firing the notification.
Most of the times leaving these calls in the setter is harmless and just causes extra notifications, but as seen in this case, it does cause a problem when updating a keypath -- as opposed to a key.
In short, if you do implement a setter, do not call will|didChangeValueForKey: inside of it.
Options cannot be 0, I think.
Its an old API, back before Apple got diligent about API design, and you have to give it something.
I'd like my controller to subscribe to notifications from view. However, before doing that, I'd like to confirm if it is OK for a view to know the instance of its controller?
Let me offer you a more specific example of what I have in mind.
My controller creates the view and informs it that it is its controller
self.gameView = [[GameView alloc] initWithController:self];
Once done, it subscribes for notifications from this view
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(saySomething:)
name:#"SaySomethingClever" object:nil];
Meanwhile the view does its thing, but when the right time comes, it posts a notification
[[NSNotificationCenter defaultCenter] postNotificationName:
#"SaySomethingClever" object:gvc];
In order for it to do it, the view needs to know the recipient of the notification (gvc).
I'd like to use this opportunity and as you whether the following is ok:
When initWithController is called, the view
-(id) initWithController: (GameViewController* )g {
gvc = g;
return [self initWithFrame:CGRectMake(0, 0, 480, 300)];
}
where initWithFrame:CGRectMake is a private method that handles specific view stuff.
Everything works fine, however, i wonder whether this approach is morally acceptable
It's not strictly a problem if the view has a reference to its controller, but it looks like your real problem is a misunderstanding of the notification posting method.
The object argument isn't the receiver. Indeed, if it were -- if the poster of a notification had to know the object that was going to get the notification -- that would defeat the entire purpose of the notification. You could just call the appropriate method! The point of notifications is that the poster doesn't need to know the other objects which are listening.
The object argument is actually used by the receiver to distinguish which notifications it should care about. Most frequently, the argument is the poster itself:
[[NSNotificationCenter defaultCenter] postNotificationName:IDidSomethingInteresting
object:self];
but it can in fact be any object.
When registering for notifications, you can specify a particular instance whose notifications you're interested in. This is the object argument to addObserver:... The notification center will then only pass on those notifications whose name and object match what was specified.
Even if you pass nil for the object in addObserver:..., you can check the object of a received notification and only act if the poster was one that you are interested in.
For example, there might be several windows in you application, and you may be interested in knowing when one of them is resized, but you don't care what happens to the rest of them. You would pass just that window instance as the object for addObserver:...
To sum up, your view in this case doesn't need that reference to its controller in order to for the controller to receive notifications posted by the view.
See also: "Posting Notifications"
While the concept is OK, it's not needed in your case:
[[NSNotificationCenter defaultCenter] postNotificationName:#"SaySomethingClever"
object:self];
The object referenced by an NSNotification is usually the object which posts a notification. The whole notification idea is that posters don't need to know about observers.
I would focus on controllers calling other controllers (or ideally model methods).
Allow each view to work with it's main resource and allow the controller for that view to make additional calls.
I've been thinking about this and have read through another stackoverflow question regarding the best recommended way to communicate between view controllers. However, the question/answer for that doesn't seem to address the best approach for the reverse behavior.
i.e. to pass data from ParentController to its ModalController, we could initialize ModalController like initWithDataToProcess:.
But what if we want to do the reverse? How would I notify the previous controller about a new data?
e.g. User clicks on 'new person' button on the ParentController. I initiate a new ModalController and present the user with a person editor view via presentModalViewController:. User clicks on 'done' to add a new person. I dismissModalViewController: and UI returns to the ParentController's view.
Using a global field reference in a singleton object (app delegate or other) is bad. delegation (via formal protocol) and notifications (via NSNotificationCenter) seems overkill. Any suggestions?
It is generally cleaner to use notifications. Just add your observer like this....
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(somethingHappened:) name:#"MyNotification" object:nil];
and elsewhere in your code you'd post the notification whenever you need to.
[[NSNotificationCenter defaultCenter] postNotificationName:#"MyNotification" object:self];
In the example I'm passing self but you can pass any object you wish really and it will be fed to your somethingHappened: function
The important thing is to keep the #"MyNotification" very descriptive and unique. Adding your project name to the beginning is a good way to keep things unique...eg. #"ProjAXViewHasGotData"
A delegate is pretty much the minimum you can do. If you think it is too much of a hassle to declare a new protocol for this, just pass in the parent view controller and have the modal one call a method on it.
I'm working on an iPhone app that performs some communication with the server to be initialized the first time it's run. I have a class ServerCommunication with methods for each of the basic tasks to be performed (authenticate user, get activation key, download updates, etc.), and a new instance of ServerCommunication is created for each task; I'm not reusing them, although it would be more efficient.
When the user completes the first initialization screen, ServerCommunication gets created four times. I keep track of it with NSLog(#"Initializing ServerCommunication instance %p", self); in its -init method. The second initialization screen also calls ServerCommunication a few times when the user taps the "Next" button, but on its last instantiation, the app hangs with the message -[ServerCommunication insertDataIntoLocalDB:]: message sent to deallocated instance 0xec75b0 in the console. The thing is, 0xec75b0 is the address of the first instance of ServerCommunication I created way back at the first screen.
Why would it be sending messages to that instance? I'm not retaining them anywhere; they're mostly autoreleased. If it helps, all of the methods in that class perform asynchronous downloading of XML data with NSURLConnection and then parse it with NSXMLParser. The parser's delegate method -(void)parserDidEndDocument:(NSXMLParser *)parser then sends off NSNotifications that are received by methods in my view controllers so they know whether to proceed to the next screen or stay there and display an error message.
Help is much appreciated!
The first thing I would do is turn on NSZombies, which should let you break at the point where your zombie is being messaged.
A common cause of problems like this is when you have objects with weak references to each other that are not allocated and deallocated at the same time. So (hypothetically), some other object stores a pointer to your ServerCommunication object as a delegate or owner. When ServerCommunication is deallocated, it doesn't unregister, and then some time down the road the object holding the weak reference tries to message you.
If I had to completely guess (and I do!) I bet you add your ServerCommunication objects as an NSNotification observer, but never remove them. Try making sure that you do this:
[[NSNotificationCenter defaultCenter] removeObserver:self];
sometime before deallocation. (It's also possible that there's a more circuitous path involving NSNotification here -- such as a pointer to the ServerCommunication object being passed as data to the view controller, which is then trying to message it.)
I created a program to send and receive events through NSNotification. Now i need to send data along with the event. Can anyone suggest me how to do this in coding in Objective-C??
There are two ways - one, you can pass any one object in with a notification - look at
+ (id)notificationWithName:(NSString *)aName object:(id)anObject
The second thing is, you can also pass an optional dictionary with as many objects as you like in it, you just need to have both sides agree on the keys used to store and retrieve the objects. That call is:
+ (id)notificationWithName:(NSString *)aName object:(id)anObject userInfo:(NSDictionary *)userInfo
You can always pass a nil for either anObject or userInfo in either call.
An example call that sends a notification directly (you don't have to construct the notification first if you do not want to):
[[NSNotifcationCenter defaultCenter] postNotificationName:#"MyNotification" object:myObjectToSend];
There's also a variant of that call with userInfo added on, just as there is for notification construction.