Re: Memory management question (passing objects to method)
Re: Memory management question (passing objects to method)
- Subject: Re: Memory management question (passing objects to method)
- From: Dietmar Planitzer <email@hidden>
- Date: Thu, 3 Mar 2005 13:22:12 +0100
On Mar 3, 2005, at 3:58 AM, Tim Lucas wrote:
On 02/03/2005, at 4:46 PM, mmalcolm crawford wrote:
That said, this pattern is unusual, and you are encouraged to use
standard accessor methods pervasively. Better than the above would
be to simply invoke:
[object setBlah:nil];
And also just to add to the above, this is the pattern you should use
in your dealloc methods (you all write delloc methods, right?!).
Sure, but not necessarily the way you recommend below :)
I would strongly advice against calling setter methods from an object's
-init or -dealloc methods. The simple reason being that the state of an
object is by definition undefined while it is being constructed or
deconstructed. This is not a problem as long as you can guarantee that
other objects are not able to inspect the state of your object while
its executing its -init or -dealloc method. However, as soon as -init
or -dealloc calls a setter, the setter may make the partially
initialized / deallocated state of your object visible to other objects
by either:
1) Sending a notification
2) Sending an KVO observation message
3) Storing data on the undo stack
Consider for example what would happen if the -setName: method below
would send a "object changed its name" notification. If the
notification gets posted by -setName: while it was invoked from
-dealloc, then every observer of the notification would get a chance to
look at your object which is currently in an undefined state because
its being deallocated. Worse, an observer may decide to retain the
notification sender (the object currently being deallocated), which
naturally will have no effect because as soon as we return from the
notification callout, our object will be freed. However, the observer
still thinks he's working with a valid object, because after all, he
retained it. Its just that that retain didn't have any effect because
the retain count was already 0 and the object was already doomed to
finish its finial march to the great object heaven. If the observer
then tries to access the object at a latter time, it will likely crash
the application. Debugging problems like this, are, well, interesting
and highly time consuming.
If -setName: would store the old name on the undo stack, then we would
have another interesting problem. The execution of -dealloc would
result in a situation where the internal guts of the object would be
spilled onto the undo stack, with no way to reconstruct the original
object when the user invokes the redo command.
Even if we would correct the above two problems by precisely
controlling when the object gets disconnected from the notification
center and undo manager, there is one thing that we can not control:
subclassing.
If our -init or -dealloc method calls a setter, assuming that our
particular implementation of that setter will work as expected and free
all instance variables associated with it, we still don't know if this
is the case for a override of our setter in a subclass. The override
may send notifications, though our implementation didn't. It may store
old instance variable values on the undo stack, although our version
didn't. Worse of all, it may introduce a caching scheme where the old
instance variable value is not immediately freed but moved to a cache.
The contents of that cache may then be leaked, if it doesn't get freed
another way.
No, -init and -dealloc are special methods in the sense that, from the
viewpoint of the outside world, they should be atomic. Atomic in the
sense that other objects do not get a chance to observe / inspect the
state of an object which is being constructed or deconstructed. As
otherwise, observers may decide to keep a reference to an object which
is about to die, or start to work with an object that is not yet fully
initialized and ready to do its job.
While I'm here, another thing about passing nil to setters: again, I
strongly advise against writing setters which accept random values
especially nils. There are two reasons for this:
1) Setters which accept everything and your mother in law, have the
tendency of hiding and propagating bugs in your application.
2) Most of the time, passing nil to a setter which takes an object has
an unclear meaning. I.e. what exactly does it mean to pass nil to the
below -setName: method ? Does it mean, remove the name ? (but what
should the UI do for such an object ?) Does it mean, set the name to
the empty string ? Does it mean, set the name to an arbitrarily chosen
default name ?
I write all my setters in such a way that they always contain a bunch
of good old assert() macro invocations at the very beginning, where I
check that the passed in value is acceptable and in the valid range. I
then let the developer build of the application always run from inside
the debugger. This has the big advantage that when one of my setters
gets called with a bogus value, that the debugger instantly stops the
execution of the application at the point of the problem and prints out
a nice stack backtrace. This makes hunting down bugs much easier as
they get no chance to propagate away from the place of their creation.
Accepting nils in setters is especially problematic in ObjC because of
that, in my opinion, highly unfortunate msg-sends-to-nil-get-ignored
feature. Both things combined can make debugging a problem very hard
because they make it so easy for nils to propagate throughout your
application code and then blowing up your application where you would
least expect it.
Just don't get me started regarding the msg-sends-to-nil-get-ignored
feature or class posing :)
Regards,
Dietmar Planitzer
For example, if you have something like the following:
- (id)init
{
if(self = [super init])
{
[self setName:@""];
}
return self;
}
- (void)setName:(NSString*)aString
{
if(aString != name)
{
[name release];
name = [aString retain]; // (or copy if you want)
}
}
then your dealloc should look like:
- (void)dealloc
{
[self setName:nil];
[super dealloc];
}
- tim lucas
_______________________________________________
Do not post admin requests to the list. They will be ignored.
Cocoa-dev mailing list (email@hidden)
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden
_______________________________________________
Do not post admin requests to the list. They will be ignored.
Cocoa-dev mailing list (email@hidden)
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden