How do you remove an observer from an object under ARC? Do we just add the observer and forget about removing it? If we no longer manage memory manually where do we resign from observing?
For example, on a view controller:
[self.view addObserver:self
forKeyPath:#"self.frame"
options:NSKeyValueObservingOptionNew
context:nil];
Previously, I would call removeObserver: in the view controller's dealloc method.
You still can implement -dealloc under ARC, which appears to be the appropriate place to remove the observation of key values. You just don't call [super dealloc] from within this method any more.
If you were overriding -release before, you were doing things the wrong way.
I do it with this code
- (void)dealloc
{
#try{
[self.uAvatarImage removeObserver:self forKeyPath:#"image" context:nil];
} #catch(id anException) {
//do nothing, obviously it wasn't attached because an exception was thrown
}
}
Elsewhere on stack overflow, Chris Hanson advises using the finalize method for this purpose and implementing a separate invalidate method so that owners can tell objects that they are done. In the past I have found Hanson's solutions to be well-thought-out, so I'll be going with that.
Related
I have an observer on an UIView to adjust a UIWebView when the size is changed (e.g. on rotation of the device):
[[self view] addObserver:self forKeyPath:#"frame" options:0 context:nil];
Naturally I need to remove this observer when the view is dismissed (otherwise there is an exception). But where would I do this? I tried in dealloc, or in viewDidDisappear, but I get a SIGABRT when I do this.
- (void) viewDidDisappear:(BOOL)animated {
NSLog(#"Is moving %d - %d", self.isMovingFromParentViewController, self.isBeingDismissed);
[[self view] removeObserver:self forKeyPath:#"frame"];
}
I tried to move it in earlier on ViewWillDisappear, but the same result.
Moreover, I am not completely happy with this later solution (even if it would work), as it will potentially also unload when the view is temporary out of view (i.e. with pageViewController), although that is not that big a deal for me at the moment.
Tracking the problem down to this item and trying to solve it has already cost me a day, so any suggestions are really appreciated!
I have a basic question regarding removing observer.
I have a ViewController parent class which is inherited by 3 ViewController child classes.
eg. BookVC -> BookHotelVC, BookFlightVC, BookTrainVC
Here, I added an observer in the viewDidLoad of parent class (I do [super viewDidLoad] in child ViewControllers) which notifies a method written in parent class. My code-
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(BookingCompleted:) name:#"BookingCompleted" object:nil];
Now I want to remove the observer when I move away from any of the child ViewControllers, but I can't write [super dealloc] in dealloc of each child ViewController because ARC doesn't permit this.
How can I remove the observer which is set ? Because whenever I move to child ViewController, a new observer is added which causes weird things (like, calling that method twice/thrice... - invoking alert twice/thrice...).
Kindly suggest.
Removing the observers in dealloc is fine, do not call [super dealloc] (as you saw, with ARC enabled, the compiler won't let you), simply write:
- (void)dealloc {
[self removeYourObservers];
}
Just don't call super! In ARC it's not required (see http://clang.llvm.org/docs/AutomaticReferenceCounting.html#dealloc).
-(void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
Normally I add my observers in viewWillAppear and remove them in viewWillDisappear. In this case I need one of the observers to continue even after the view is gone so that it can finish some work. In order to make sure that the observer is only added once with this view, I do the following:
-(void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:YES];
[[NSNotificationCenter defaultCenter]removeObserver:self
name:#"imageSaved"
object:nil];
[[NSNotificationCenter defaultCenter]addObserver:self
selector:#selector(postMessageWithImage:)
name:#"imageSaved"
object:nil];
}
I have performed a search through the rest of the application to ensure that this observer is NOT registered anywhere else. Unfortunately sometimes, but not all times and there is no consistent factor, the notification is fired twice. I have also ensured with breakpoints and NSLog that the postNotifcationName is NOT called more than once. I have not been able to reproduce on the iPhone as the problem seems confined to the iPad.
In further troubleshooting I have checked that the method is being called from the same thread (no reason it wouldn't be but just to check). This problem DOES go away if I put the removeObserverin viewWillDisappear, however, again that is not how I need this to work.
Clearly this is a case where the observer for this is being registered twice but I cannot find a reason why that would be. As you can see from the code, any time this observer is registered it is first removed. My only other thought is whether self could get "corrupted" such that the removeObserverwouldn't function properly?
Add your observer when the view will show, and remove it when will disappear.
ADD:
- (void)viewDidLoad
{
[[NSNotificationCenter defaultCenter]addObserver:self
selector:#selector(postMessageWithImage:)
name:#"imageSaved"
object:nil];
}
REMOVE:
- (void)postMessageWithImage:(NSNotification*)aNotification
{
[[NSNotificationCenter defaultCenter]removeObserver:self
name:#"imageSaved"
object:nil];
// here do your job
}
This is perfectly valid and efficient.
Instead of adding the observer in viewWillAppear:, try adding the observer you wish to persist when the view disappears in viewDidLoad. Then you can call your removeObserver:name:object: in your dealloc method
If you just want something to be executed once, put it in the predicate of a dispatch_once() call, like
static dispatch_once_t lock;
dispatch_once(&lock, ^{
// put your addObserver call here
});
I'm trying subclass a UIView in which I want to add more subviews. Since I'm changing frame property of my view in code, I want my subviews to resize when this view is resized, so I've added the following code in my initWithCoder: method:
[self addObserver:self forKeyPath:#"frame" options:NSKeyValueObservingOptionNew context:nil];
And in the observation method, I'll reset view's frame manually. Everything works fine, but when I push some other view controller and move back, I got this message:
Observation info was leaked, and may even become mistakenly attached to some other object.
So, how can I fix this? I know for sure that the observer is not removed, but there is not any viewDidDisappear stuff here. What should I do?
Thanks!
You should remove the observer for all added observers, So do like following code,
- (void)dealloc {
[self removeObserver:self forKeyPath:#"frame" context:NULL];
}
I have added NSNotificationCenter in viewDidLoad method and removed in viewDidUnload but it's not getting removed. I am following ARC. I have followed few answer but I didn't get luck. I dont have reputation for give comments so posting some thing looks like duplicate. Please don't -ve votes.
Sample code:
- (void)viewDidLoad
{
[[NSNotificationCenter defaultCenter ] addObserver:self.containerView
selector:#selector(loadInitialScreen)
name:CLEARSCREEN_DEPOSIT
object:NULL];
}
- (void)viewDidUnload
{
[[NSNotificationCenter defaultCenter] removeObserver:self.containerView
name:CLEARSCREEN_DEPOSIT
object:nil];
}
You should remove the observer either in -viewWillDisappear:, -viewDidDisappear: or in the -dealloc method, depending on your needs. The reason is -viewDidUnload in iOS6+ is never called anymore and before iOS6 it's called when a memory warning is received.
Try to use viewDidDisappear instead viewDidUnload :
-(void)viewDidDisappear:(BOOL)animated
{
[[NSNotificationCenter defaultCenter] removeObserver:self.containerView
name:CLEARSCREEN_DEPOSIT object:nil];
[super viewDidDisappear:animated];
}
viewDidUnload is called (for < iOS 6.0), when a memory warning is received to the application/view controller.
It will not be called for removal of the view, for that dealloc is called. But as you are using ARC, you cannot implement dealloc method.
The best bet is to remove the observer in the method loadInitialScreen, if it has to be called only once.
If your notification can be posted multiple times, it's better to remove the observer in viewDidDisappear, but then add observer for the notification in ViewWillAppear