When observing a value on an object using addObserver:forKeyPath:options:context:, eventually you'll want to call removeObserver:forKeyPath: on that object to clean up later. Before doing that though, is it possible to check if an object actually is observing that property?
I've tried to ensure in my code that an object is only having an observer removed when it needs to be, but there are some cases where it's possible that the observer may try to remove itself twice. I'm working to prevent this, but just in case, I've just been trying to figure out if there's a way to check first if my code actually is an observer of something.
[...] is it possible to check if an object actually is observing that
property?
No. When dealing with KVO you should always have the following model in mind:
When establishing an observation you are responsible for removing that exact observation. An observation is identified by its context—therefore, the context has to be unique. When receiving notifications (and, in Lion, when removing the observer) you should always test for the context, not the path.
The best practice for handling observed objects is, to remove and establish the observation in the setter of the observed object:
static int fooObservanceContext;
- (void)setFoo:(Foo *)foo
{
[_foo removeObserver:self forKeyPath:#"bar" context:&fooObservanceContext];
_foo = foo; // or whatever ownership handling is needed.
[foo addObserver:self forKeyPath:#"bar" options:0 context:&fooObservanceContext];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if (context == &fooObservanceContext) {
// handle change
} else {
// not my observer callback
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
- (void)dealloc
{
self.foo = nil; // removes observer
}
When using KVO you have to make sure that both objects, observer and observee, are alive as long as the observation is in place.
When adding an observation you have to balance this with exactly one removal of the same observation. Don't assume, you're the only one using KVO. Framework classes might use KVO for their own purposes, so always check for the context in the callback.
One final issue I'd like to point out: The observed property has to be KVO compliant. You can't just observe anything.
Part of the NSKeyValueObserving protocol is this:
- (void *)observationInfo
which should list the observers.
EDIT
Useful for debugging only.
I underestand this an objective-c question. But since lots of people use Swift/objective-c together, I thought I point out the advantage of the Swift4 new API over older versions of KVO:
If you do addObserver multiple times for KVO, then for each change you’ll get the observeValue as many as the current number of times you’ve added yourself as the observer.
And to remove yourself you have to call removeObserver as many times as you added.
Removing it more than you’ve added will result in a crash
The Swift4 observe is far smarter and swiftier!
If you do it multiple times, it doesn’t care. It won’t give multiple callbacks for each change.
And only one invalidate of the token is enough.
invalidating it before beginning to observer or more times that that you’ve done observe will not result in a crash
So to specifically answer your question, if you use the new Swift4 KVO, you don't need to care about it. Just call invalidate and you're good. But if you're using the older API then refer to Nikolai's answer
Related
How do I subscribe to objects being added and removed from a NSMutableDictionary using ReactiveCocoa? Also, I'd like to broadcast a notification when it changes. My guess is that broadcasting can be done using RACMulticastConnection but how do I tie this up with the dictionary change? I'm trying to use ReactiveCocoa for the first time in my project and stuck on the first thing I wanted to do :(
RACObserve is a wrapper around key-value observing, and inherits the same features and flaws.
Unfortunately, NSMutableDictionary is not automatically observable. There are two ways to work around that:
Subclass it and add KVO support.
Create a real model object, with properties instead of dictionary keys. Then you'll get KVO on those properties, as long as you use setters instead of direct ivar modification.
I'm not sure exactly what you mean by "[broadcasting] a notification when it changes," or why it'd be valuable. Notifications are way too global for my taste, and I'd promote using more limited observation instead (like KVO).
However, assuming you definitely want to do this, it's simple enough to post a notification in response to a new signal value:
#weakify(self);
[RACObserve(self, dictionary) subscribeNext:^(NSDictionary *dictionaryValue) {
#strongify(self);
[NSNotificationCenter.defaultCenter postNotificationName:SomeNotificationName object:self];
}];
If you want KVO's change dictionary (which includes information about the added/removed values), you'll need to replace RACObserve with +rac_valuesAndChangesForKeyPath:options:observer:.
every time you set or remove the key-value,reset the dict,so you can observer the dict.
just like:
[RACObserve(self, testDict) subscribeNext:^(id x) {
NSLog(#"RACObserve testDict:%#",x);
}];
[self.testDict setObject:value forKey:key];
self.testDict=self.testDict;
How can I find out when an object is being released? I am listening for kvo changes, but the object get's deallocated before the retain count goes to 0, and I get the following warning:
An instance 0x16562be0 of class MyViewController 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:
Basically what I'm trying to do is to detect when the model is dismissed. I can't use a Delegate, because the viewControllers being presented are dynamic, and my mainViewController has no knowledge about them other than the fact that they are subclasses of UIViewController.
[anotherViewController addObserver:self forKeyPath:#"retainCount" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionOld | NSKeyValueObservingOptionPrior context:nil];
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
// Here check for the changes and see of the new value is 0 or not
}
I also tried listening for the superView of the viewController being changed to nil
[anotherViewController.view addObserver:self forKeyPath:#"superView" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionOld | NSKeyValueObservingOptionPrior context:nil];
You can only do Key-Value Observing on keys for which the object supports it. What you want to do here is simply not possible — an object's observers are all supposed to be gone by the time it gets to dealloc. You will need to structure your application such that either this object is kept around as long as it is needed or it actively tells interested parties before it goes away.
And looking at an object's retainCount is just never a good idea. As far as it is useful, it is only useful for debugging — and even then there are much better and more reliable tools. The result of retainCount is simply misleading, and it does not work the way most people expect. Watching for it to be 0 is an exercise in futility, because no object can exist with a retain count of 0 — when an object with a retain count of 1 is released, it gets deallocated, and then you are not allowed to message it anymore. (In fact, the framework literally has no way of representing a 0 retain count because it's an unreachable state.)
UPDATE
As of late 2017 (iOS 11, macOS 10.13), when an object is deallocated, it automatically unregisters any remaining observers. From the Foundation release notes for that year:
Relaxed Key-Value Observing Unregistration Requirements
Prior to 10.13, KVO would throw an exception if any observers were
still registered after an autonotifying object's -dealloc finished
running. Additionally, if all observers were removed, but some were
removed from another thread during dealloc, the exception would
incorrectly still be thrown. This requirement has been relaxed in
10.13, subject to two conditions:
The object must be using KVO autonotifying, rather than manually
calling -will and -didChangeValueForKey: (i.e. it should not return NO
from +automaticallyNotifiesObserversForKey:)
The object must not
override the (private) accessors for internal KVO state
If all of these are true, any remaining observers after -dealloc
returns will be cleaned up by KVO; this is also somewhat more
efficient than repeatedly calling -removeObserver methods.
And as of late 2020 (iOS 14, macOS 10.16), KVO is even more careful when an object still has observers during deallocation:
Key-Value Observing
New Features in iOS & iPadOS 14 beta 5
Key-Value Observation removal facilities now employ deterministic
bookkeeping methods. Cases that would have produced hard-to-diagnose
crashes, especially those where KVO signals problems accessing
deallocated observer pointers or observers associated with incorrect
objects, now produce an exception pinpointing which observed object
needs a missed removeObserver(_:) call, and by which observers. This
exception was previously thrown as ‘best effort’ when KVO could detect
the problem; the new deterministic bookkeeping allows it to be thrown
for all cases where removeObserver(_:) is needed.
The improved determinism also allows improved Swift API handling.
Instances of NSKeyValueObservation, produced by the Swift
NSObject.observe(_:changeHandler:) method, take advantage of
integration with this bookkeeping so they now invalidate automatically
when the observed object is released, regardless of how the object
implements its KVO behavior. This applies to all usage of this API in
macOS 11 Big Sur beta, including on processes built with previous
versions of the SDK, and eliminates certain classes of crashes that
sometimes required using the legacy API instead. (65051563)
ORIGINAL
There are a few problems here.
One problem is that you asked the wrong question. You meant to ask “How do I deregister my observer at the right time, before the target is deallocated?” Instead, you mentioned retainCount, which tends to provoke people into berating you about using retainCount instead of helping you do what you're trying to do, which is deregister your observer at the right time.
Another problem is that your view controller doesn't own its model (meaning it doesn't have a strong reference to the model). Usually you want your view controller to own its model, to prevent exactly this sort of problem. While your view controller exists, it needs a model to operate on, so it should own the model. When the view controller is being deallocated, it should stop observing its model and release it. (If you're using ARC, it will release the model automatically at the end of dealloc). You might also choose to deregister in your viewWillDisappear: method, if your view controller goes on and off of the screen repeatedly.
Note that an object can be owned by multiple other objects simultaneously. If you have several view controllers operating on the same model, they should all own the model, meaning that they should all have strong references to the model.
A third problem is that you're (probably) using KVO directly. The built-in KVO API is not very pleasant to use. Take a look at MAKVONotificationCenter. This KVO wrapper automatically unregisters an observer when the observer or the target is deallocated.
if you are interested in getting notified when an object gets deallocated you could send a notification in dealloc, but don't reference the object getting dealloc'ed.
for instance
[[NSNotificationCenter defaultCenter] postNotificationName:#"myclass_dealloced" \
object:[NSValue valueWithPointer:self]];
but you wouldn't ever want to dereference that pointer...
use this only for debugging and testing.
Trying to automatically de-register observers during dealloc is too late.
When dealloc is called, the state of the object graph is undefined. Specifically, order of deallocation is typically not guaranteed and may often change in light of asynchronous processes and/or autorelease.
While the graph the deallocating object strongly references should be coherent, that'll quickly change as the object is deallocated.
The same holds true for the observer of the object being deallocated; as deallocation of an object graph happens, the observed objects state may likely change. As it changes, it may cause observers to fire while the object graph is in the inconsistent, being deallocateed, state.
You really need to concretely separate deallocation from observation logic.
That is, when your controller is dismissed from screen, it should actively dismiss the model layer, including tearing down any observers (or notifying any observers that the model layer is about to go away).
Your observers need to de-register their notifications at the same time they let go of the object.
For example, if your objects are registering notifications on one of their properties, de-register all the notifications before the property is changed or set to nil.
There never should be "hanging" notification registrations to objects that have been simply lost track of. How can you deregister your notifications if you lose track of the object?
just do what KVO says. Observe, act accordingly and signal the Key manually when you need to. That way you can of course know when an object gets deallocated.
When you removeObserver from the Object and it is already deallocated then the method call is acting on nil which does no harm or your observing object holds still a reference and in such case you can still act accordingly.
With ARC this is not a problem and one of the great benefits.
Test it yourself..
// public header.
#interface ObjectToBeObserved : NSObject
#end
// declare in private header
// because you dont want to allow triggering from outside
#interface ObjectToBeObserved : NSObject
// use some artificial property to make it easy signalling manually.
#property (nonatomic) BOOL willDealloc;
#end
#implementation ObjectToBeObserved
-(void)dealloc {
[self willChangeValueForKey:#"willDealloc"];
[self didChangeValueForKey:#"willDealloc"];
}
#end
In your Observer side you just do classic KVO design pattern..
void* objectDeallocatedContext = & objectDeallocatedContext;
#implementation ObservingObject {
// easy to see you could even make a protocol out of the design pattern
// that way you could guarantee your delegate has such property to observe
__weak ObjectToBeObserved *delegate;
}
-(instancetype)initWithObservableDelegate:(ObjectToBeObserved*)observable {
if (!(self=[super init])) return nil;
delegate = observable;
// see i use observe old value here..
if (delegate!=nil)
[delegate addObserver:self forKeyPath:#"willDealloc" options:(NSKeyValueObservingOptionOld) context:objectDeallocatedContext];
return self;
}
-(void)dealloc {
if (delegate!=nil)
[delegate removeObserver:self forKeyPath:#"willDealloc" context: objectDeallocatedContext];
}
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
if (context==objectDeallocatedContext) {
NSLog(#"the observed object deallocated");
// in theory you hold still a weak reference here
// which should be nil after this KVO signal arrived.
// the object in the signal therefore might not be valid anymore,
// which is what you want when observing deallocation.
}
}
#end
KVO is a signal pattern, not a way to know if a signalling object is still valid. But when the object is gone it will not signal anything, those when you can receive the signal you are just fine. Because i choose to watch the NSKeyValueObservingOptionOld value with a void* context, it gets even signaled before the objects artificial "willDealloc" property is set (well, not even set). The KVO can arrive without a valid object but has still a context to compare to. You just need the ping
My AppDelegate maintains a list of active window controllers to avoid ARC deallocating them too early. So I have a notification handler like this:
- (void) windowWillClose: (NSNotification*) notification {
[self performSelectorOnMainThread: #selector(removeWindowControllerInMainThread:)
withObject: windowController
waitUntilDone: NO];
}
- (void) removeWindowControllerInMainThread: (id) windowController {
[windowControllers removeObject: windowController];
}
I use the main thread because doing the handling on the notification thread risks deallocating the controller before it's ready.
Now, this works pretty well — except when there are animators currently running. I use animators in some places, through NSAnimationContext. I have looked at this QA, and the answer just isn't acceptable. Waiting for a while, just to get animation done, is really shoddy and not guaranteed to work; indeed it doesn't. I tried using performSelector:withObject:afterDelay, even with a larger delay than the current animation duration, and it still results in the animator running against nil objects.
What is the preferred way of doing controller cleanup like this? Not use NSAnimationContext but using NSAnimation instead, which has a stopAnimation method?
First, if some of your animations run indefinitely -- or for a very long time -- you're going to have to have a way to stop them.
But for things like implicit animations on views, you could simply use a completion method.
self.animating=YES;
[NSAnimationContext runAnimationGroup:^(NSAnimationContext *context){
[[v animator] setAlphaValue: 1];
} completionHandler:^{
self.animating=NO;
}];
Now, you only need to poll whether your animation is running and, if it's not running, proceed to close your window.
One nice way to do the polling is to set a timer with a fixed delay. If the animation is still running, just reset the timer and wait another interval.
Alternatively, you could send a notificaton from the completion handler.
I haven't used NSAnimationContext (always did this with NSAnimation, but mostly for historical reasons). But the typical way I like to managed things similar to this is to create short-lived retain loops.
Mark's answer is exactly the right kind of idea, but the polling is not required. The fact that you reference self in the completion handler means that self cannot deallocate prior to the completion handler running. It doesn't actually matter whether you ever read animating. ARC has to keep you around until the completion block runs because the block made a reference to you.
Another similar technique is to attach yourself to the animation context using objc_setAssociatedObject. This will retain you until the completion block runs. In the completion block, remove self as an associated object, and then you'll be free to deallocate. The nice thing about that approach is that it doesn't require a bogus extra property like animating.
And of course the final, desperate measure that is occasionally appropriate is to create short-lived self-references. For instance:
- (void)setImmortal:(BOOL)imortal {
if (immortal) {
_immortalReference = self;
}
else {
_immortalReference = nil;
}
}
I'm not advocating this last option. But it's good to know that it exists, and more importantly to know why it works.
I'm trying to write this down as concisely as possible, but it's not easy to describe -- so thanks for reading =)
I'm the main developer of the Open Source iPhone Framework Sparrow. Sparrow is modeled after the Flash AS3 Library, and thus has an event system just like AS3. Currently, that system works by specifying selectors - but I would love to expand that system by allowing the use of blocks for event listeners. However, I'm stumbling over memory management issues.
I will show you a typical use-case of events - as they are handled now.
// init-method of a display object, inheriting from
// the base event dispatcher class
- (id)init
{
if (self = [super init])
{
// the method 'addEventListener...' is defined in the base class
[self addEventListener:#selector(onAddedToStage:)
atObject:self
forType:SP_EVENT_TYPE_ADDED_TO_STAGE];
}
return self;
}
// the corresponding event listener
- (void)onAddedToStage:(SPEvent *)event
{
[self startAnimations]; // call some method of self
}
That's quite straight-forward: When an object is added to the display list, it receives an event. Currently, the base class records the event listeners in an array of NSInvocation-objects. The NSInvocation is created in a way that it does not retain its target and arguments. (The user can make it do that, but in 99% of the cases, it's not necessary).
That these objects are not retained was a conscious choice: otherwise, the code above would cause a memory leek, even if the user removed the event listener in the dealloc-method! Here is why:
- (id)init
{
if (self = [super init])
{
// [self addEventListener: ...] would somehow cause:
[self retain]; // (A)
}
return self;
}
// the corresponding event listener
- (void)dealloc
{
// [self removeEventListener...] would cause:
[self release]; // (B)
[super dealloc];
}
On first sight, that seems fine: the retain in the init-method is paired by a release in the dealloc method. However, that does not work, since the dealloc method will never be called, because the retain count never reaches zero!
As I said, the 'addEventListener...'-method does, for exactly this reason, not retain anything in its default version. Because of the way events work (they are almost always dispatched by 'self' or child objects, which are retained anyway), that is not a problem.
However, and now we come to the central part of the question: I cannot do that with blocks. Look at the block-variant of event handling, as I would like it to have:
- (id)init
{
if (self = [super init])
{
[self addEventListenerForType:ADDED_TO_STAGE block:^(SPEvent *event)
{
[self startAnimations];
}];
}
return self;
}
That looks great and would be very easy to use. However: when the user calls a method on 'self' or uses a member variable within the block -- which will be, well, almost always -- the block will automatically retain 'self', and the object will never be dealloc'ed.
Now, I know that any user could rectify this by making a __block reference to self, like this:
__block id blockSelf = self;
[self addEventListenerForType:ADDED_TO_STAGE block:^(SPEvent *event)
{
[blockSelf startAnimations];
}];
But, honestly, I am sure almost all of the users would not know to do so or forget to do so. An API should be not only easy to use, but also hard to misuse, and this clearly violates that principle. Users of the API would most definitely misuse it.
What bugs me is that I know that 'self' does not have to be retained -- it works in my current implementation without retaining it. So I want to tell the block that he does not need to retain self -- me, the library, should tell the block that, so that the user does not have to think about it.
In my research, I have not found a way to do so. And I can't think of a way to change my architecture to fit that limitation of blocks.
Has anybody got an idea what I could do about it?
Even if you have not, thanks for reading this far -- I know it was a verbose question ;-)
I discussed this topic with Apple support, and they told me what I had expected: currently, there is no way that would let me tell the block that it should not retain self. However, they offered two ways to work around the problem.
The first would be to pass self as an argument to the block rather than a block variable. The API would become:
[self addEventListenerForType:ADDED_TO_STAGE
block:^(id selfReference, SPEvent *event)
The API would thus be responsible for passing in the (non-retained) self. Developers would still have to be told that they have to use this reference instead of self, but at least it would be easy to use.
The other solution that Apple has used in this sort of situation is to provide a separate method from release/dealloc for shutting down the listener. A good example of this is the "invalidate" method of NSTimer. It was created because of a memory cycle between NSRunLoop and NSTimer (by design).
I think the design is fundamentally flawed, blocks or no blocks. You are adding objects as event listeners before they are fully initialised. Similarly, the object could still receive events while it is being dealloced. The adding and removing of event listeners should be decoupled from allocation/deallocation IMO.
As for your immediate issue, you may not need to worry about it. You are having a problem because you are implicitly adding a reference to an object to itself (indirectly) by referencing it in the block. Bear in mind that anybody else supplying a block is unlikely to be referencing the object that generates the event or its instance variables because they won't be in scope where the block is defined. If they are (in a subclass, for instance), there's nothing you can do except document the issue.
One way to reduce the likelihood of a problem is to supply the object generating the event as a property on your SPEvent parameter and use that in the block instead of referencing self. You'll need to do that anyway because a block need not be created in a context where the object generating the event is in scope.
In a NSManagedObject Sub Class I have the code …
- (void) awakeFromInsert {
[self addObserver:[NSApp delegate] forKeyPath:#"name" options:NSKeyValueObservingOptionNew context:nil];
}
Which adds my App Delegate as an Observer, what I want to do now is from inside my App Delegate, I want to remove itself as an Observer for my NSManagedObject Sub Class.
How would I do this?
Thanks.
I was thinking of adding this to my App Delegate
[JGManagedObject removeObserver:self forKeyPath:#"name"];
but unfortunately removeObserver:forKeyPath: is not a Class Method.
For something like this, it's probably best to rethink the design. The delegate, in this case, would have to have some specific knowledge of the managed object itself in order to do this -- and the delegate would have to have some idea about when in the lifecycle it should (or would want to) stop observing the object.
You have a few choices. Instead of doing this in awake from insert, you could have the delegate start observing it when it creates it and then stop observing it when it gives up ownership. If that is not feasible in your design, you could have the object remove its observer when it is deallocated. If this is a fire-and-forget (basically the delegate only cares once), you could remove the observer after the first change notification. Since, however, you created the observation within the creation lifecycle of this object, it is probably best to remove that observation at the destruction of the object:
- (void)dealloc
{
[self removeObserver:[NSApp delegate] forKeyPath:#"name"];
// other clean-up
[super dealloc];
}
You might also want to do this when the object awakes from fetch and from fault and release the observer when the object will become a fault.
Much the same way you added the observer in the first place, only with fewer options:
// Given some managed object "object"...
[object removeObserver:self forKeyPath:#"name"];
Note that we remove self as the observer, rather than the application delegate as given by [NSApp delegate], since the code will be running within the delegate itself.
How about sending your object the removeObserver:forKeyPath message just before you delete it from the ManagedObjectContext?
From iOS 11 and above, we have automatic registration of KVO. From the Foundation Release Notes for macOS 10.13 and iOS 11
Relaxed Key-Value Observing Unregistration Requirements
Prior to 10.13, KVO would throw an exception if any observers were still registered after an autonotifying object's -dealloc finished running.
Additionally, if all observers were removed, but some were removed
from another thread during dealloc, the exception would incorrectly
still be thrown. This requirement has been relaxed in 10.13, subject
to two conditions:
• The object must be using KVO autonotifying, rather than manually
calling -will and -didChangeValueForKey: (i.e. it should not return NO
from +automaticallyNotifiesObserversForKey:) • The object must not
override the (private) accessors for internal KVO state
If all of these are true, any remaining observers after -dealloc
returns will be cleaned up by KVO; this is also somewhat more
efficient than repeatedly calling -removeObserver methods.