I'm uisng addObserver:selector:name:object: in viewDidLoad.
And I'm using removeObserver:name:object: in viewWillDisappear:animated: to remove observer.
What will happen if I failed to remove observer by passing wrong parameter to removeObserver:name:object:?
(For example, observer isn't removed if I pass wrong notification to parameter name or wrong object to object or Observer)
If the observer still not nil after calling removeObserver:name:object:, I can find out that removing observer failed because notificationSelector will being called.
But if the observer become nil after calling removeObserver:name:object:, I can not find out whether removing observer failed or not.
Will observers automatically removed when observer become nil?
Or does notification dispatch table of NSNotificationCenter became larger and larger and eventually the app become slow?
EDIT
When I use subclass of UIViewController object for observer, the app doesn't crash after ViewController's dealloc are called.
But when I use a object of other class, the app crashs after the object's dealloc are called.
Update: From -[NotificationCenter removeObserver:]:
If your app targets iOS 9.0 and later or macOS 10.11 and later, you don't need to unregister an observer in its dealloc method. Otherwise, you should call this method or removeObserver:name:object: before observer or any object specified in addObserverForName:object:queue:usingBlock: or addObserver:selector:name:object: is deallocated.
Old answer:
Observers are not removed automatically. From the NSNotificationCenter Class Reference:
Important: The notification center does not retain its observers,
therefore, you must ensure that you unregister observers (using
removeObserver: or removeObserver:name:object:) before they are
deallocated. (If you don't, you will generate a runtime error if the
center sends a message to a freed object.)
You should therefore call
[[NSNotificationCenter defaultCenter] removeObserver:self];
in your dealloc method if you are not 100% sure that the observer was not removed previously.
You just need to put in the correct Observer for the Observer to be removed. If you pass the wrong parameter to name or object (or nil), the receiver will not use them as criteria for removal.
All Cocoa programs have a default NSNotificationCenter, so once you remove the observers you shouldn't have to worry about the it taking up more memory.
Related
I have a UIViewController which has objects perform a series of tasks on background threads as it appears, and it's registered as an observer to them. When they get called for the first time, it stops being an observer.
I realised I could save user time if these tasks were performed before the view controller is shown, so I initialized it and called a method which ran these tasks.
Then I started getting errors along the lines of:
An instance [insance] of class [class name] was deallocated while key value observers were still registered with it.
How can I prevent this from happening? If i show the view controller straight away, this works no problem.
I'd recommend, that you add a call [notificationCenter removeObserver: self] in method dealloc of those classes, which you intend to use as observers, as it is the last chance to unregister an observer cleanly.
That's all I can get from the crash when I do in the applicationDidBecomeActive
MyStoreObserver * observer = [[MyStoreObserver alloc] initWithContext:self.managedObjectContext andDelegate:self];
[[SKPaymentQueue defaultQueue] addTransactionObserver:observer];
But the weird thing is I have been able to purchase twice in the past and even if I delete the app re-reun everything I cannot escape this pit. It doesn't seems logic. I can alloc init the observer alone without problem and I can call the default paymentQueue without problem but if I try to add the transaction observer to the queue, life stop. It's on ARC and the delegate I added to the observer is not the problem. I know for sure.
I've added an nslog to every method in the observer and none get called before the crash
SOLUTION FOUND
Okay, well it looks like the observer is not retained by the queue and thus the observer needs to be an instance variable.
Thanks
Okay, well it looks like the observer is not retained by the queue and thus the observer needs to be an instance variable (or retained in some way).
To add an official source to this answer:
Inside the header file SKPaymentQueue.h of the Framework Storekit is the following clear comment:
// Observers are not retained.
// The transactions array will only be synchronized with the server while the queue has observers.
// This may require that the user authenticate.
- (void)addTransactionObserver:(id <SKPaymentTransactionObserver>)observer __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_3_0);
If I add an observer to the [NSNotificationCenter defaultCenter] in my viewDidLoad should I be removing it in viewDidUnload?
If you need to add these in your initializer, you should remove it in the dealloc method. Ideally, you should only care about these notifications when you are currently onscreen or not.
The viewDid[Appear|Disappear] methods can be called multiple times during the lifetime of a UIViewController. Register for the notification in the viewDidAppear method and unregister it in viewDidDisappear.
You should remove it in dealloc method.
It seems to me viewDidUnload is the place to put it.
If the notification handler that gets called accesses any of the views managed by the view controller, that will either be an error or will cause the view to get reloaded unnecessarily. If your view is not being shown, then most likely the view controller doesn't need to be notified. If it does, at least check if the view is loaded before you make any changes to it. While the view is not loaded, you might still need to update the state of your view controller, for example change or dirty cached values, but don't update the view until it loads again.
Two, what happens if you don't removeObserver in viewDidUnload, and viewDidLoad gets called again? You call addObserver again. Probably doesn't hurt, the notification center can detect duplicate adds.
I subclass MPMoviePlayerController. In that class I attached all possible notifications that I need. DidFinishPlayback, ExitFullScreen etc. in it's loading method. My question is, if I want to STOP movie and dismiss movie player view can I (and do I need to) remove observers in moviePlayerPlaybackStateDidChange method on stateStopped? What can happen if I don't do that?
The most important place to remove an observer of any kind is in the dealloc method. It is best practice to remove the observers as soon as your done observing but absolutely needs to be done by dealloc.
The reason for this is because if you register as an observer for something and your class gets deallocated the object you were observing could possible try and callback to the now deallocated object. More than likely this will cause an EXC_BAD_ACCESS and close your application.
When I add an observer to the default notification center, where would I unregister that?
Example: I have a UIView subclass which lives inside a view controller. That subclass is an observer for the FooBarNotification. If this notification is posted, that view will get it. But now, the view controller decides to throw away the view. Is the best place the -dealloc method of the view itself?
Are there any rules like memory management rules? For example: Must I unregister an observer where I registered it? i.e. the view registers itself in it's init method, so it should unregister itself in it's -dealloc method?
(not talking about push notifications, but NSNotificationCenter)
The only rule is to make sure the observer lives longer than the registration period.
Since -addObserver:… will not -retain the observer, if the registration outlives the observer itself, your program will crash.
Apple doesn't specify any rules to unregister the observer. -dealloc is fine. Just use common sense. E.g. if that observer may persist even after the view controller throw it away, then you should unregister at that throw-away procedure, otherwise the observer may receive unwanted notifications.
Certainly it should be done in the dealloc, but basically when you no longer need notifications.
One possible place is to override the willMoveToWindow: message on the UIView and remove the notification when the view is removed from a window:
- (void)willMoveToWindow:(UIWindow *)newWindow
{
if (window == nil) {
// view removed from a window -- remove notifications here
}
[super willMoveToWindow:newWindow];
}
I usually add my observers in viewWillAppear and remove them again in viewWillDisappear.
This works because almost always I won't want a notification to be picked up by a view that isn't currently in the foreground (any longer living observers I register in my coordinator)
Dealloc and viewDidUnload if you have registered for notifications in viewDidLoad.