Re: Get error message about registered observers when Object receives dealloc message
Re: Get error message about registered observers when Object receives dealloc message
- Subject: Re: Get error message about registered observers when Object receives dealloc message
- From: Roland King <email@hidden>
- Date: Sat, 29 Aug 2009 21:00:12 +0800
Well first off you're not really, really doing anything wrong, that
message is in the wrong place (in my opinion), it should only come up
when the NSObject dealloc is called if things haven't been unobserved.
There's a comment in this thread about this having been fixed,
hopefully it is.
Still there's a couple of comments I'd make. Again I can fully
understand why you've done it this way and I can easily imagine doing
just the same. However observation is a sort of loose-coupling
protocol, as you can see even though you don't retain your observee,
you've kind of made a coupling with it which is orthogonal to dealloc.
Normally an observee has no idea who's observing it and normally the
observer will retain the observee to stop it going away, and remove
its own observation in its dealloc just before releasing the object it
had been observing. If it goes that way around, you don't have a
problem.
So is there a way you can turn your retain graph upside down so that
children retain their parents (and observe them) and that's what keeps
the parents alive? Then you have your observation and retention aligned.
If you can't then, given the way you describe your design, you're in a
position where the parent actually knows what its children are .. in
which case .. KVO isn't necessarily the right protocol for you, if you
know who your children are you could easily have them all implement a
protocol you yourself design and sends them the changes directly. Then
you don't have this issue at all, there's no KVO which is going 'the
wrong way' and needs to be torn down in dealloc and in fact you have a
direct coupling to your 'observers'. You also save all the setup and
tear down and any other overhead of KVO.
Something like that would of course not be as 'free' as KVO, you'd
need to actually write the code to send out the changes, in which case
you may have decided that KVO is better than writing your own direct
protocol in which case .. you've already sort of done what I suggested
in the last paragraph, you've just used KVO to do it for you. In that
case personally I believe you haven't really done anything wrong and
the message is just in the wrong place. If that's been fixed, you
probably don't have a problem.
On Aug 29, 2009, at 7:30 PM, Andreas Grosam wrote:
On Aug 28, 2009, at 5:32 PM, Roland King wrote:
Thank you Roland for your reply.
I think this is one of those unfortunate messages which comes out
before the dealloc method even runs, just entering the dealloc
method with registered observers logs it. It's possible it would be
better if it was only logged at the point say NSObject dealloc was
called, but it's not, it's called at the start of your own dealloc.
That either indicates it's a bug in the message because it doesn't
allow for the case where a dealloc method unregisters the things
observing it .. or perhaps you're not really supposed to be doing
it there. Normally an object will register its interest in
observing another object and the same object will then unregister
later. It's a little unusual for an object to unhook the objects
which are observing it. Actually how are you even doing that, do
you have a list of all the objects and paths which are observing you?
Actually, the provided code is a over-simplification. My object
hierarchy looks as follows:
Model ( provides the properties where other objects can register
for observing it)
dictionary of objects that are observers
The "child" objects contain a weak reference to its parent Model
(Model is not retained, otherwise I would establish a circular
reference).
During setup, child objects register itself as observers, observing
one or more properties of its parent object Model:
- setup {
...
[self.model addObserver: self
forKeyPath: key
options: NSKeyValueObservingOptionInitial
context: NSSelectorFromString([ports
objectForKey:key])];
...
}
When Model gets deallocated, it releases the dictionary, which
releases the observer objects, which in turn invokes their dealloc
method:
- (void) dealloc {
[self.model removeObserver:self forKeyPath:key];
...
}
I can sort of see how this might happen in practice, you create an
object who's lifetime is determined by external factors, but
observers hook up to it without retaining it and .. do something
with observations of the key properties. Eventually the object gets
released because all the other things which cared about it release
it and you want to remove the observations and dealloc is the
natural place to do that.
Yes, that may describe it. Actually, as shown above, there is an
object hierarchy, where child objects are owned by its respective
parent object. The child object's life time is (shall be) controlled
by the parent object. However, there may be times where other objets
get references of the child objects and retain it. Due to this, the
logic in the code shall ensure that all "external" retained
references have been released when the parent object `Model' gets
deallocated. So, at the time Model receives -dealloc -- all child
object's retain count shall equal *one*. If not, horrible things
will happen ;)
I'd try to get it the other way around myself if possible, have the
observer retain the object observed and register the observation,
then have it drop the observation at a later point and release the
object, which then eventually deallocs. I sort of understand how
that could be hard if your object lifecycle is complicated and you
were trying to use the implicit reference count to manage object
lifetime and have the observers not retain and have a sort of weak
reference.
The object hierarchy and its interconnections are quite complex.
There are also animations and timers involved that invoke messages
from a different context. And timers also retain certain child
objects - or some sub-objects of them. So, tearing down the Model -
requires a number of steps to be performed in the right order and
synchronously.
Is there something other than dealloc you can use to denote when
everything except observers have finished with your object so you
can set an observed property which basically says "I'm useless and
want to die" and causes any observers to remove the observation and
release the object, which will then dealloc?
I'm currently think about a method - something like "shutdown" which
helps to orderly tear down an object hierarchy. However, in order to
work, this requires a set of conventions that need to be followed,
e.g.:
- There is a protocol, Shutdownable (ugly name). All objects within
the hierarchy must implement it.
- The method shutdown needs to be invoked from a parent object for
each of its child object from within its own shutdown method. (this
will establish a recursive invocation scheme)
- The method shutdown must be invoked for an object before its
parent's dealloc method gets invoked.
- If the base class implements a shutdown method, [super shutdown]
must be called.
- The implementation of shutdown must ensure that its actual
shutdown actions must be performed only once - even when the method
gets invoked several times.
- When an object receives shutdown, the only valid message that can
be send afterward to it is -release.
- It's considered an error when an object receives shutdown and
there are other objects than the parent object that hold a reference
to it. However, the objects may be simply reject actions that may be
invoked after the object received shutdown.
- Everything in the logic is thread safe.
Well, this is what comes up to my mind. Maybe this is the way I have
to go. What do you think?
Regards
Andreas Grosam
On Aug 28, 2009, at 9:56 PM, Andreas Grosam wrote:
I'm using key-value-observing where an instance of class
MyObservee has been registered for KVO with other objects which
observe a value in a key path (e.g.: @"drives.model.port"):
The observee itself unregisters all observers in its dealloc method:
@implementation MyObservee
- (void) dealloc
{
[self removeAllObservers]; // basicly: [self
removeObserver:observer forKeyPath:key];
[super dealloc];
}
The observers are sill alive when the observee receives its
dealloc message.
When the observed instance receives its dealloc message, I'm
getting this error message in the console, before the first line
of code in the dealloc method will be executed (note: BEFORE
[super deallocate] has been invoked):
2009-08-28 14:57:49.753 MyApp[886:20b] An instance 0xd21b60 of
class MyObservee is being deallocated while key value observers
are still registered with it. Observation info is being 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 0xd38e00> (
<NSKeyValueObservance 0xd39880: Observer: 0xd356e0, Key path:
drives.model.port, Options: <New: NO, Old: NO, Prior: NO> Context:
0x16df0, Property: 0xd38990>
...
The class MyObservee does NOT have a sub class - that is, [super
dealloc] will not be called somewhere prematurely.
The base class of MyObservee is NSObject.
Am I doing something wrong here?
Thanks in advance for hints.
Regards
Andreas Grosam
_______________________________________________
Cocoa-dev mailing list (email@hidden)
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden
_______________________________________________
Cocoa-dev mailing list (email@hidden)
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden
_______________________________________________
Cocoa-dev mailing list (email@hidden)
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden