Re: Best Way to Handle Properties?
Re: Best Way to Handle Properties?
- Subject: Re: Best Way to Handle Properties?
- From: Ken Thomases <email@hidden>
- Date: Wed, 20 Aug 2008 07:30:38 -0500
On Aug 20, 2008, at 6:05 AM, Dave wrote:
This makes memory management awkward. This code is creating an
object using alloc, so it's responsible for releasing it. However,
you're not keeping a pointer to the new string you've created.
You're just passing it to the PersonDetails object and then
forgetting it. So, you can't release it.
Yes, this is what worried me. The set method (setFirstName) stores
the NSString pointer inside itself, and the "reset" method releases
it in this case. The reason I did this is because if I were to
specify "copy" instead "assign" for the property, then there would
be two NSString Objects allocated one here and one inside the "set"
method. I was trying to avoid allocating two Objects, e.g. the
method would look something like this:
-(void) setFirstName:(NSString*)theNewValue
{
if (FirstName != theNewValue)
FirstName = [theNewValue copy];
}
Or am I missing something?
You're engaging in premature optimization. Get it right first, then
worry about performance (after measuring!).
You also overlooked the option to have the property use "retain".
Please review the memory management guidelines and conventions. The
code you presented created an object, so it's responsible for making
sure it's released. The PersonDetails class has its own separate
responsibilities. It must ensure that its properties stick around for
as long as it needs them, so it should either retain or copy anything
passed into it because it doesn't have any other way of being sure
about such objects' lifetimes. Naturally, whatever it retains or
copies it then has the responsibility to release.
The setter you wrote just above is wrong because it fails to release
whatever might have been in the FirstName ivar when it was invoked.
Thus, it will leak. Apple provides several possible forms that
accessors typically take in their documentation. Unless you have a
good reason to deviate, you should follow those guidelines. Even
better, allow the compiler to synthesize accessors whenever possible.
There are a few approaches to fixing this. First, you can use a
local variable to hold a pointer to the new object. Then, after
passing it to setFirstName: you can release it. Second, you can
use alloc/init/autorelease where you currently use alloc/init.
How do I specify autorelease?
You invoke the autorelease method. In your original code snippet, you
had something of the form:
[personDetails setFirstName:[[NSString alloc] init...]];
To autorelease the string that you're creating and then losing track
of, you could do:
[personDetails setFirstName:[[[NSString alloc] init...] autorelease]];
Third, you can use a convenience method which creates a string for
you but doesn't leave you responsible for releasing it, such as
+stringWithCharacters:length:.
What is the difference between -stringWithCharacters:length: and
+stringWithCharacters:length: ? Looking at the documentation it's
not obvious (to me anyway) what the difference is.
Well, the former doesn't exist. ;)
A plus at the beginning of a method name indicates that it's a class
method. A minus means it's an instance method.
Also, +stringWithCharacters:length: is a convenience constructor.
That is, it returns to you a new object but one that you're not
responsible for releasing because, technically, you're code didn't
create it; the convenience constructor created it, so that code has
the responsibility for arranging that it will eventually be released.
You said earlier that you had read about memory management. I think
you need to review the memory management guide. This subject is one
that most people need to read about several times before it really
"clicks".
So, I guess the best way to do it would be to Allocate and Free a
new "PersonDetails" object each time through the loop
That will work, although you don't "free" objects, you release them
and they decide when it's time to deallocate themselves (because no
other strong references to them remain; i.e. their retain count
reaches zero).
, or have the "ReadPersonString" method allocate and return a
"PersonDetails" object. The "Reader" Object could then keep track of
the "PersonDetails" object and free it on subsequent calls to
"ReadPersonString" and when the file is closed.
If you go that route, then the reader should autorelease the
PersonDetails objects it returns. I wouldn't recommend a scheme where
the reader keeps track of the objects it has returned to free them
later. That's what autorelease pools are for.
Firstly in the real code there is a "PersonDataValid" flag so I can
tell if it's a good "Person" or not, secondly, unless I put a whole
load of logic in the "PersonDetails" object and/or have it know
about all the reader objects, it can't initialize a "salient"
object. Or do you mean something else?
I mean the initializer method should take as arguments all of the
pieces of information required for a Person (or PersonDetails) object
to be valid. Generally, if an object exists (is successfully
allocated and initialized), it should be valid. If it can't be made
valid, then the initializer should fail (return nil). If there are
different possible combinations of information that could make a valid
Person, then that suggests you want to have several different
initializers, each taking different sets of arguments. If you do,
please make sure you understand the notion of designated initializer
and how all of your other initializers should funnel through that one.
In some cases, it makes sense to design an initializer that takes a
_source_ of information. In that case, the initializer knows how to
extract the information it needs from the source. As you worried, a
bad design of this type might require the class to know intimate
details of several such sources of information, resulting in close
coupling between classes and violations of encapsulation. However,
it's possible to make a fairly general design of this type. In
particular, Cocoa already has one: the NSCoder/NSCoding mechanism.
You might consider using this approach for your design. See the
Archives and Serializations Programming Guide for Cocoa.
When you're done with a given Person object, you release it. If
you need a new one, you allocate and initialize a new one.
Ok, I think this is covered above. One more question. If I were to
release a "PersonDetails" object, would the NSString's be released?
If not, do I have to implement my own release method?
Since you should make the PersonDetails object own its properites, it
should have the responsibility for releasing them when it's
deallocated. You achieve this by implementing a -dealloc method on
the class. Again, see the memory management guide.
Cheers,
Ken
_______________________________________________
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