I use the following line to add an observer:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(itemDidFinishPlaying) name:AVPlayerItemDidPlayToEndTimeNotification object:self.playerItem];
My observer (self) will never be deallocated.
But when the user starts playing a different item, the old self.playerItem will be deallocated---but will always be replaced with a new one, which I want to continue observing.
When that happens, what happens in regards to my observer's status as an observer? Do I need to do something to stop observing the deallocated object, as is required with KVO? Or will I continue observing the new object at self.playerItem? Or will my observer automatically be "unregistered?"
If I need to remove the observer, I wonder why there's no corresponding removeObserver method that enables one to specify a selector; it seems I can remove an observer only wholesale via removeObserver:(id)notificationObserver.
According to the NSNotificationCenter class reference:
Be sure to invoke removeObserver: or removeObserver:name:object:
before notificationObserver or any object specified in
addObserver:selector:name:object: is deallocated.
So: you should unregister your observer before self.playerItem deallocated.
But when the user starts playing a different item, the old
self.playerItem will be deallocated---but will always be replaced with
a new one, which I want to continue observing.
You may pass nil as the last parameter of addObserver:selector:name:object: method:
Adds an entry to the receiver’s dispatch table with an observer, a notification selector and optional criteria: notification name and sender.
If you don't specify
If you pass nil, the notification center doesn’t use a notification’s sender to decide whether to deliver it to the observer.
So you will receive notification AVPlayerItemDidPlayToEndTimeNotification from any object that posts it.
Since iOS 9 it is no longer needed to remove an observer from an object:
In OS X 10.11 and iOS 9.0 NSNotificationCenter and NSDistributedNotificationCenter will no longer send notifications to registered observers that may be deallocated.
However, block based observers need to be un-registered as before:
Block based observers via the -[NSNotificationCenter addObserverForName:object:queue:usingBlock] method still need to be un-registered when no longer in use since the system still holds a strong reference to these observers.
More information can be found here:
https://developer.apple.com/library/content/releasenotes/Foundation/RN-FoundationOlderNotes/index.html#10_11NotificationCenter
Related
Is there a way to see if an object is already an observer for a type of notification?
Currently, each addObserver call is paired with removeObserver to avoid duplicate observers, but is there a way to see if an object is already an observer before invoking addObserver?
NSNotificationCenter.defaultCenter().removeObserver(self, name: CustomEvent, object: foo)
NSNotificationCenter.defaultCenter().addObserver(self, selector: #("test"), name: CustomEvent, object: foo)
Unfortunately no, there is not. Just like KVO, notification center doesn't provide an API that lets us check whether an object (self in this case) has already been registered as an observer or not.
You will have to yourself keep track using an bool variable and set it to "true" when you make an addObserver call and reset it when you call removeObserver.
Call addObserver again only when the bool is set to "false".
There is no other way to figure out if object is already an observer.
Could someone kindly clarify the purpose of notificationSender in the addObserver function of NSNotification?
Here's the explanation from the Apple docs:
notificationSender
The object whose notifications the observer wants to receive; that is, only notifications sent by this sender are delivered to the observer.
If you pass nil, the notification center doesn’t use a notification’s sender to decide whether to deliver it to the observer.
We use notifications to respond when a video has ended. The code:
NSNotificationCenter.defaultCenter().removeObserver(self, name: AVPlayerItemDidPlayToEndTimeNotification, object: playerItem)
where playerItem contains the video in question. However, passing nil for object seems to have no discernible effect.
It's better for us to use nil instead of playerItem because we wouldn't need to create another class variable.
What's the risk in using nil, and what's the advantage in using playerItem?
The only reason to be specific with the notificationSender is if you have chosen a notification name that other objects (that you don't want to listen to) are going to send to you. Specifying a notificationSender is akin to a delegate relationship, where leaving it nil means that absolutely any object could send you a notification with the same String and you would process it.
In practice, this is rarely an issue as long as you pick notification strings that are unique. You'll see people using things like com.my.app.name.notification or kMyAppNotification for precisely this reason.
And as to the specific line of code you linked, I don't actually know the implementation details, but I'm assuming if you added a listener with the notificationSender property pointing to an object, you also need to remove the same listener with the notificationSender property set, or else risk a memory leak by not removing observers. But I'd have to read the docs again to figure that out.
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
i have an NSNotificationCenter selector,
where to put it ? in the delegate (if yes then where?) in the controller?
where to put the method as well.
do i need to dealloc the NSNotificationCenter ?
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(deviceNotificationReceived:) name:UIApplicationDidBecomeActiveNotification object:nil];
- (void)deviceNotificationReceived:(NSNotification *)notification
{
[self.soundMgr endInterruption];
}
The deviceNotificationReceived: method must be an instance method of the argument to addObserver:. It is self in this instance, so your method should go in the same class.
You should not release the NotificationCenter, as you did not create or retain it.
Your question was a little hard to understand, is this what you were asking?
Hi, i have an NSNotificationCenter selector,
okay, you mean you have a selector for a method in NSNotificationCenter.
In Objective-C, “selector” has two
meanings. It can be used to refer
simply to the name of a method when
it’s used in a source-code message to
an object. It also, though, refers to
the unique identifier that replaces
the name when the source code is
compiled.
http://developer.apple.com/mac/library/documentation/cocoa/....../ocSelectors.html
So you have created a selector that refer to a method.
where to put it ?
It's a variable, you can store it where ever you feel it fits in your design.
in the delegate
See above.
(if yes then where?)
It's a variable, it depends on your usage.
in the controller?
Do you have controller? Depends on your design.
where to put the method as well.
Which method?
do i need to dealloc the NSNotificationCenter ?
No, [NSNotificationCenter defaultCenter] returns a reference to the notification center, you don't dealloc it.
Since you are subscribing to the UIApplicationDidBecomeActiveNotification notification, the most logical place to put the notification is in the applicationdDidFinishLaunching method of your app delegate.
That's the first point your code gets called, so you cannot set it earlier.
where to put it ?
It depend on when you need to register for notification. One way is to add observer in 'init' method of the class and remove notification in 'dealloc'method of the class.
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.