Re: How far with accessors?
Re: How far with accessors?
- Subject: Re: How far with accessors?
- From: Jeff Biggus <email@hidden>
- Date: Thu, 27 May 2004 18:11:23 -0500
My claim in a nutshell (since this is a long email):
[self setObject:nil] in place of [myObject release] in -dealloc is not
good technique.
On May 27, 2004, at 6:02 AM, Ondra Cada wrote:
>
If the instance uses and/or "contains" a helper object, the exact way
>
it stores (the reference to) it and the exact way it handles it should
>
be encapsulated in accessors, and wherever you work with the object,
>
you should use this encapsulation, *without presuming how it actually
>
looks like*. Therefore, the code should generally look somewhat like
>
this:
>
[...]
>
-init { [self setFoo:@"whatever"]; }
>
-printFoo { NSLog(@"%@",[self foo]); }
>
-(void)dealloc { [self setFoo:nil]; }
My problem is only with the dealloc method. I have given this some more
thought and how found several reasons why this way of dealloc is both
poor style and leads to more fragile code:
1. Using a setter method to dealloc an object, mixes two different
functions in one method. (poor isolation of functionality)
2. One should avoid unnecessary messaging, especially when one is, say,
creating and destroying many (thousands or millions of) objects.
(inefficiency)
3. Sending "nil" to a setter method should not, in general, release the
object and then set its pointer to nil (and dealloc shouldn't count on
it either). For strings, for instance, It makes more sense to set the
string to an empty value, but not to free the object entirely. In
general, you won't know whether or not the value being sent to a setter
at runtime is nil. Consider [theObject setString:[someObject
someMethod]]. Now if the result of someMethod is nil, do you want the
default behavior to be to completely dealloc the string? In more
generic objects, I would make sending a nil value to a setting cause
that to set a default value, not to deallocate the object. (fragile
code)
4. Having a destructor method (dealloc) use an externally-available
method (the setter) leads to more fragile, less easily-maintained code.
Dealloc should do its own work directly. How does one ensure that
subclassing won't break a dealloc method, as Finlay mentioned?
(fragility)
5. One could rewrite the setter method and then have to chase down bugs
because the dealloc method was trying to use a loophole to do its job.
This can be troublesome in projects with many programmers or in objects
which use many source files. (fragility)
On May 26, 2004, at 7:06 PM, Ondra Cada wrote:
>
You seem to miss that my code does not "simply nil", but nil through
>
the setter. Since the setter *must* be written so that it releases the
>
previous value before setting the new one, bingo!
Things break if I switch to an NSMutableString from an NSString and
decide not to release anything in my setter. Yet nothing breaks if you
just don't try to use a setter for a dealloc's job.
-(void)setString:(NSString *)newString {
[myString setString:[NSString stringWithString:newString]];
}
This doesn't handle a nil message in either case. I'm guessing you'd
say that one should then just put in an if/else clause to deallocate
the string. But this is adding a whole other functionality to the
method. Besides, the more natural behavior seems to be something like
this:
-(void)setString:(NSString *)newString {
if (newString != nil)
[myString setString:[NSString stringWithString:newString]];
else
[myString setString:@""];
}
Or one could put any other message in there like, @" -- no information
given --", etc, following the idea that nil could be used to set up
defaults. One may then access it as a true (nsmutable)string without
the placebo of sending messages to a nil object.
Having to always keep in the back of my mind that dealloc has its own
requirement for all my setter method seems to be bad design.
On May 27, 2004, at 6:02 AM, Ondra Cada wrote:
>
> [self setTheString:nil] implies that theString exists and that its
>
> *value* is being set to nil. However, [theString release] is clearly
>
> releasing the *object*.
>
>
Wrong level of abstraction. [self setTheString:nil] in fact implies
>
that the instance's property named "foo" is being removed. The
>
concrete way of removing it is (quite properly) encapsulated in the
>
accessor code.
Using a setter function to deallocate an object is, no offense, a hack.
If you want to make a publically available method which can deallocate
an internal instance variable (which I'm frankly surprised anyone would
want to do in such a public place as a setter method), then write a
very tiny specific method for just such a process and then make use of
it in the dealloc method if you want to.
>
> It also seems to requires assumptions made about the setter's
>
> internal code, which the act of releasing an object should be
>
> oblivious to.
>
>
Again, it's exactly the opposite: [foo release] is what makes unneeded
>
assumptions about the setter internal code. If you just use the setter
>
to remove the property, dealloc would work properly whatever the
>
accessors do (presumed they are not buggy, of course ;)). On the other
>
hand, had you use just [foo release] instead of the proper [self
>
setFoo:nil], the simplest change in accessors may break your code.
This is incorrect. The dealloc code would be unaffected by any
correctly written setter.
>
For example, you may decide to use the quicker (and less robust)
>
accessor pattern
>
-foo { return foo; }
>
-(void)setFoo:f { [foo autorelease]; foo=[f copy]; }
>
In the "your" way, you have to remember to change the your dealloc to
>
[foo autorelease].
This is incorrect. Firstly, one would use retain instead of copy,
typically. In either case, that setter method requires no change in
dealloc. [foo release] will be fine. Also, no one should need [foo
autorelease] in a dealloc method, whatever the setter method.
>
Or consider that later on you decide to move the property into a
>
dictionary (for whatever reason, say, for an easy XMLization):
>
-foo { return [dict objectForKey:@"foo"]; }
>
-(void)setFoo:f { if (f) [dict setObject:f forKey:@"foo"]; else [dict
>
removeObjectForKey:@"foo"]; }
>
In all such cases, a setter used in dealloc (and, generally, accessors
>
consistently used through your code) would continue working properly.
>
Direct references to the instance variable would break.
Again, nothing would break without using your setter method. Actually,
the above else clause is unnecessary with a dealloc method which is
simply going to dump the whole "dict" object anyway.
>
> (One should be able to freely rework setter methods without thinking
>
> about how someone might be using them to deallocate objects.)
>
>
Very definitely so. That's the *exact* reason why you should use the
>
accessors anywhere, including in dealloc. (Of course, all rules have
>
its exceptions, and there are good cases sometimes to access ivars
>
directly; but, like it is with all optimizations, it should be done
>
*ONLY* if there are strong reasons to, *never* otherwise.)
I'm curious as to where this idea that sending nil to a setter method
to dealloc is common. I've read many Objective-C and Cocoa books
without it once seeing it used. Is this a technique common in another
language? Just being curious here, not trying to insult your crafty
ways.
-Jeff
_______________________________________________
cocoa-dev mailing list | email@hidden
Help/Unsubscribe/Archives:
http://www.lists.apple.com/mailman/listinfo/cocoa-dev
Do not post admin requests to the list. They will be ignored.