KVO and ARC how to removeObserver
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.
Is NSNotificationCenter removeObserver in ARC needed?
You should explicitly remove the observer even you use ARC
. Create a dealloc
method and remove there..
-(void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
If you see the method you don't need to call [super dealloc];
here, only the method without super dealloc needed.
UPDATE for Swift
You can remove observer in deinit method if you are writing code in swift.
deinit {
NSNotificationCenter.defaultCenter().removeObserver(self)
}
In ARC do we need to send removeObserver: explicitly?
Yes, you need to call removeObserver:
, if you don't the observed class could call all deallocated instance of the observer.
How to remove an KVO observer added with observe() API?
Example of using key-value observing
/// define an Observer
var observation: NSKeyValueObservation?
override func viewDidLoad() {
super.viewDidLoad()
/// start observation
observation = view.observe(\.backgroundColor, options: [.old, .new], changeHandler: { (view, value) in
})
/// invalidate observation
observation?.invalidate()
observation = nil
}
Do I have to removeObserver in KVO manually
You must call -removeObserver:forKeyPath:
manaully. iOS will not do it automatically.
Apple said does not maintain strong references to the observing object
. I think it means, if you want to removeObserver for a temp var out off the temp var's scope, you should make the temp var as ivar, so you maintain the ivar's strong references.
If you do not call -removeObserver:forKeyPath:
. You will make : 1) Something leak
such as you code like this :
[self addObserver:a forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil];
if you do not call -removeObserver:forKeyPath:
. It will console that :
An instance 0x756a1d0 of class MyClass was deallocated while key value observers were still registered with it. Observation info was
leaked, and may even become mistakenly attached to some other object.
Set a breakpoint on NSKVODeallocateBreak to stop here in the debugger.
Here's the current observation info:
[NSKeyValueObservationInfo 0x7574f60] (
[NSKeyValueObservance 0x7574f20: Observer: 0x7568280, Key path: pageCount, Options: [New: YES, Old: NO, Prior: NO] Context: 0x0,
Property: 0x7574fa0]
)
When you debug it, you will find : The self
and the a
are not leaking. The leaking thing is the NSKeyValueObservationInfo object
If you do not call -removeObserver:forKeyPath:
. You will make : 2) Intermediate class never destroy && Infinity notification
As the Apple document about KVO says:
When an observer is registered for an attribute of an object the isa
pointer of the observed object is modified, pointing to an
intermediate class rather than at the true class.
When you removeObserver, if no observer is registered, the intermediate class will destroy. And If you not call removeObserver, the intermediate class will never destroy and when you change the property, the setter method of intermediate class will continue to send notifications.
Cocoa bindings and KVO, unregister the observer, when the observing object gets `dealloced`
Update 2017
As @GregBrown has pointed out in the comments the original 2013 answer does not work in 2017. I assume the original answer did work in 2013, as my practice is not to answer without testing, but I no longer have any code I used.
So how do you do solve this in 2017? The simplest answer is swizzling, which some will find a contradiction but it need not be when using blocks. Below is a quick proof-of-concept with the following caveats:
This is not thread safe. Consider what might happen if two or more threads execute the code at the same time. Standard techniques will address that.
Efficiency was not a consideration! For example, you might wish to swizzle
dealloc
once per class and keep a list of observer/keypath in a per-instance associated object.This code only supports auto-removal, you cannot manually choose to remove an observer. You might wish to change that.
The code:
@implementation AutoRemovedKVO
typedef void (*DeallocImp)(id, SEL);
+ (void)forTarget:(NSObject *)target
addObserver:(NSObject *)observer
forKeyPath:(NSString *)keyPath
options:(NSKeyValueObservingOptions)options
context:(nullable void *)context
{
// register the observer
[target addObserver:observer forKeyPath:keyPath options:options context:context];
// swizzle dealloc to remove it
Class targetClass = target.class;
SEL deallocSelector = NSSelectorFromString(@"dealloc");
DeallocImp currentDealloc = (DeallocImp)method_getImplementation( class_getInstanceMethod(targetClass, deallocSelector) );
// don't capture target strongly in block or dealloc will never get called!
__unsafe_unretained NSObject *targetPointer = target;
void (^replacementBlock)(id self) = ^(__unsafe_unretained id self)
{
if (self == targetPointer)
[targetPointer removeObserver:observer forKeyPath:keyPath];
currentDealloc(self, deallocSelector);
};
class_replaceMethod(targetClass, deallocSelector, imp_implementationWithBlock(replacementBlock), "v@:");
}
@end
Both uses of __unsafe_unretained
are to work around consequences of ARC. In particular methods usually retain their self
argument, dealloc
methods do not, and blocks follow the same retain-as-needed model. To use a block as the implementation of dealloc
this behaviour needs to be overridden, which is what __unsafe_unretained
is being used for.
To use the above code you simply replace:
[b addObserver:a forKeyPath:keyPath options:options context:NULL];
with:
[AutoRemovedKVO forTarget:b addObserver:a forKeyPath:keyPath options:options context:NULL];
Allowing for the above caveats the above code will do the job in 2017 (no guarantee for future years!)
Original 2013 Answer
Here in outline is how you can handle this, and similar situations.
First look up associated objects. In brief you can attach associated objects to any other object (using objc_setAssociatedObject
) and specify that the associated object should be retained as long as the object it is attached to is around (using OBJC_ASSOCIATION_RETAIN
).
Using associated objects you can arrange for an observer to be automatically removed when the observed object is deallocated. Let X be the observer and Y be the observed objects.
Create an "unregister" class, say Z, which takes (via init) an X & Y and in its dealloc
method does removeObserver
.
To setup the observation, X:
- Creates an instance of Z, passing itself and Y.
- Registers itself as an observer of Y.
- Associates Z with Y.
Now when Y is deallocated Z will be deallocated, which will result in Z's dealloc
being called and unregistering the observation of X.
If you need to remove the observation of X while Y is still active you do this by removing the associated object - and doing so will trigger its dealloc
...
You can use this pattern whenever you want to trigger something when another object is deallocated.
HTH
About KVO different behavior in iOS10 and iOS11 without `removeObserver:forKeyPath:`
I'm confused why I didn't call "removeObserver:forKeyPath:" it shows different result on different sdk version.
That would be because different SDK versions are different. They behave differently. That is what “different” means.
Please read the release notes, https://developer.apple.com/library/archive/releasenotes/Foundation/RN-Foundation/index.html :
Prior to 10.13 [and iOS 11], KVO would throw an exception if any observers were still registered after an autonotifying object's
-dealloc
finished running.
Related Topics
Ios: Pre Install Ssl Certificate in Keychain - Programmatically
iOS Open Youtube App with Query (Url Schemes)
Ios: Launch Image Multiple Language
Update Label from Background Timer
How to Create a Big, Red Uibutton with iOS
Uibezierpath Triangle with Rounded Edges
How to Display a Uipopoverview as a Annotation to the Map View? (iPad)
Uiviewcontentmodescaleaspectfill Not Clipping
Mkmapview Mkpointannotation Tap Event
Minimum and Maximum Date in Uidatepicker
Nsdateformatter and Current Language in iOS11
What Describes the Application Delegate Best? How Does It Fit into the Whole Concept
How to Round Corners of Uiimage with Hexagon Mask
Segue in Skscene to Uiviewcontroller
What Is "Error in _Connection_Block_Invoke_2: Connection Interrupted" in iOS
Upload Image to Server - Swift 3
How to Create a User with Multiple Attributes in Firebase with Swift