Kvo and Arc How to Removeobserver

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:

  1. Creates an instance of Z, passing itself and Y.
  2. Registers itself as an observer of Y.
  3. 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



Leave a reply



Submit