Objective-c - NSMutableString setString vs NSString - objective-c

Is it a better practice, in a setter, to retain and release NSString as follows :
-(void) setName:(NSString *)newName
{
if(newName != nil)
{
[newName retain]:
[m_Name release];
m_Name = newName; //Where m_Name is a NSString *
}
//I'm not sure for this code, I have difficulties understanding memory-management in ObjC
}
Or to change the value via a NSMutableString :
-(void) setName:(NSString *)newName
{
if(newName != nil)
[m_Name setString:newName]; //Where m_Name is a NSMutableString *
}
If any or both of the methods are incorrect(s), let me know.

A couple of thoughts:
Best practice is to not write setters at all, to take advantage of the automatically synthesized accessor methods). Writing your own is only an opportunity to mess up your memory management or introduce a bug. You should have a compelling need for a custom setter before you write one.
The emerging convention for instance variable names is to use the property name preceded by a leading underscore (e.g., for a property called name, the ivar is _name). If you omit the #synthesize statement, the compiler included with recent versions of Xcode will do this automatically for you.
The question of what the setter should be makes no sense in the absence of your stating what memory qualifiers your property had. I'm going to assume you defined your property as retain.
Changing the property to a NSMutableString changes the behavior of your property and I would not advise that unless you really needed a mutable string for some reason.
Your first example does nothing if someone sets the name property to nil. But if someone wants to set it to nil, you should still (a) release the old name value; and (b) set your ivar to nil. (By the way, my code below takes advantage of the fact that sending a message to a nil object does nothing, so I don't need to check if it's nil or not, in this case.)
So, I assuming you have a property defined as follows:
#property (nonatomic, retain) NSString *name;
and a synthesize line that is either omitted or looks like:
#synthesize name = _name;
Then, I think the setter would look like:
-(void) setName:(NSString *)name
{
// if you want to program defensively, you might want the following assert statement:
//
// NSAssert(name == nil || [name isKindOfClass:[NSString class]], #"%s: name is not string", __FUNCTION__);
if (name != _name)
{
[_name release];
_name = name;
[_name retain];
}
}
By the way, I assume your init method properly initializes _name and that dealloc releases it.
- (id)init
{
self = [super init];
if (self) {
_name = nil;
}
return self;
}
- (void)dealloc
{
[_name release];
[super dealloc];
}
As bblum points out, it's prudent to use copy for your NSString properties:
#property (nonatomic, copy) NSString *name;
Then, I think the setter would look like:
-(void) setName:(NSString *)name
{
// if you want to program defensively, you might want the following assert statement:
//
// NSAssert(name == nil || [name isKindOfClass:[NSString class]], #"%s: name is not string", __FUNCTION__);
if (name != _name)
{
[_name release];
_name = [name copy];
}
}
But really, you simply shouldn't be writing setters unless you absolutely need to.
Finally, your code has a comment about finding memory management confusing. While you definitely need to understand it, I'd make two final suggestions:
Consider using Automatic Reference Counting (ARC). While this doesn't obviate the need to understand how memory management works (see the Advanced Memory Management Programming Guide), it does make it easier to write code that handles memory management properly. If you write manual reference counting (MRC) code, it's very easy to make simple memory management mistakes that ARC would otherwise take care of for you.
Especially if you're going to use MRC, avail yourself of Xcode's static analyzer ("Analyze" on the "Product" menu or press shift+command+B). This can facilitate finding many routine memory management issues that plague MRC code. The Finding Memory Leaks section of the Instruments User Guide also illustrates how to find leaks while debugging your code, but often the static analyzer can identify issues just by examining your code.

The first solution is better. This is how retain properties are handled. You retain the new value and then release the old value. Also if the nil case is not crucial to be handled, you can depend on the default implementation, generated by #synthesize.
For the second solution, it's really unnecessary, and it's a little bit against convention. Also, in this solution, you have to initialize m_name before ever assigning any string to it. You can do that in init.
- (void) init {
if (self = [super init]) {
m_name = [[NSMutableString alloc] init];
}
}
Conclusion: First solution is definitely better.

Related

Should I dealloc a nonnull property; and if so, how?

I'm exposing a few properties from an Objective-C project to Swift (based on this repo), but have no experience in Objective-C, so I'm rather out of my depth here, so please bear with me.
I'm wondering how to correctly dealloc a nonnull property (or whether it's necessary at all!). I've provisionally dealloc'ed the nonnull property surface by setting it to null (in the same manner as is done for the nullable partOfSpeech). However, this prompts the following warning:
Null passed to a callee that requires a non-null argument
... so I wonder whether it's redundant. Is there anything I should do instead to handle my nonnull property, during the Node class's dealloc block?
Given the interface, node.h:
#interface Node : NSObject {
NSString *surface;
NSString *partOfSpeech;
}
#property (nonatomic, retain, nonnull) NSString *surface;
#property (nonatomic, retain, nullable) NSString *partOfSpeech;
- (nullable NSString *)partOfSpeech;
#end
... And the implementation, node.m:
#implementation Node
#synthesize surface;
#synthesize partOfSpeech;
// surface is assumed to be set post-initialisation.
- (void)setPartOfSpeech:(NSString *)value {
if (partOfSpeech) [partOfSpeech release];
partOfSpeech = value ? [value retain] : nil;
}
- (NSString *)partOfSpeech {
if (!features || [features count] < 1) return nil;
return [features objectAtIndex:0];
}
- (void)dealloc {
// WARNING: "Null passed to a callee that requires a non-null argument"
self.surface = nil;
self.partOfSpeech = nil;
[super dealloc];
}
#end
... And given that a Node's lifecycle is like this:
Node *newNode = [Node new];
newNode.surface = [[[NSString alloc] initWithBytes:node->surface length:node->length encoding:NSUTF8StringEncoding] autorelease];
// ... Do stuff with newNode (eg. add to array of Node)...
[newNode release];
First: The compiler can automatically synthesize instance variables and
setters/getters for your properties. So your interface should be just
// Node.h
#interface Node : NSObject
#property (nonatomic, retain, nonnull) NSString *surface;
#property (nonatomic, retain, nullable) NSString *partOfSpeech;
#end
and no #synthesize statements are needed in the implementation file.
The compiler will automatically create instance variables
_surface and _partOfSpeech, and also create accessor methods
- (NSString *) surface;
- (void)setSurface:(NSString *)value;
- (NSString *)partOfSpeech;
- (void)setPartOfSpeech:(NSString *)value;
which do "the right thing", with or without ARC. You can override
those methods if you want to implement some custom logic, but you don't have to implement a standard setter like your setPartOfSpeech.
If you use ARC (automatic reference counting) then that is all,
nothing more is needed. And
I would really recommend to do so. The compiler inserts the required retain/release calls at compile time, and is quite clever in avoiding
unnecessary calls. See for example
Confirmed: Objective-C ARC is slow. Don’t use it! (sarcasm off)
about some comparisons. With MRC (manual reference counting), your code might even be slower, or
have memory leaks.
But to answer your question: With MRC you have to release the
instance variables in dealloc
- (void)dealloc {
[_surface release];
[_partOfSpeech release];
[super dealloc];
}
as explained in Memory Management Policy in the "Advanced Memory Management Programming Guide".
You should not use the accessor methods in dealloc as in your
self.surface = nil;
self.partOfSpeech = nil;
see Don’t Use Accessor Methods in Initializer Methods and dealloc.
If you are using manual memory management you can just release the object stored in the properties backing variable. As you've named the backing variable the same as the property use the -> to clearly reference the backing variable:
[self->surface release];
Or if you want to do this with assignment just assign the empty string literal:
self.surface = #"";
The string literal is created at compile time, lives throughout the program execution, and takes up very little space. The assignment will caused the release (and deallocation if the reference count reaches zero) of the previous value in the property, just like assigning nil (or any other value).
HTH

debugging objective c memory leak with xCode Leaks

I'm doing my first steps in finding memory leaks in xCode 4.5 and using the Leaks instrument. I found a couple of issues and seemed to fix them, but this one eludes me.
Here is the code:
RUBEImageInfo* imgInfo = [[[RUBEImageInfo alloc] init] autorelease];
NSString *nm = [NSString stringWithUTF8String:img->name.c_str()];
imgInfo->name = nm;
[imgInfo->name retain]; // I'm using it outside of this method
Leaks reports a leak in the second line, with the percentage next to the "i" at %100.
So I tried two things:
One, I marked nm with autohrleas like this:
NSString *nm = [[NSString stringWithUTF8String:img->name.c_str()] autorelease];
Two, I also tried calling release on nm after it's assignment to imgInfo->name so the code looks like this:
imgInfo->name = nm;
[imgInfo->name retain];
[nm release];
But in both cases the app crashes with BAD_ACCESS when I run it, and call [imgInfo->name UTF8String].
What am I missing?
EDIT following Rob's answer:
This is the RUBEImageInfo class:
#import "cocos2d.h"
#interface RUBEImageInfo : NSObject {
#public CCSprite* sprite; // the image
#public NSString* name; // the file the image was loaded from
#public class b2Body* body; // the body this image is attached to (can be NULL)
#public float scale; // a scale of 1 means the image is 1 physics unit high
#public float angle; // 'local angle' - relative to the angle of the body
#public CGPoint center; // 'local center' - relative to the position of the body
#public float opacity; // 0 - 1
#public bool flip; // horizontal flip
#public int colorTint[4]; // 0 - 255 RGBA values
}
#end
And the .m:
#import "RUBEImageInfo.h"
#implementation RUBEImageInfo
// Nothing much to see here. Just make sure the body starts as NULL.
-(id)init
{
if( (self=[super init])) {
body = NULL;
}
return self;
}
-(void) dealloc {
[name release];
[super dealloc];
}
#end
A couple of reactions:
Instruments identified where the leaked object was allocated, but in this case, this code might not be the source of the leak. You should:
ensure you release the name in the dealloc method of RUBEImageInfo; and
also, if you're setting name a second time, make sure you release the previous name object before you set it to a new object.
Your life will be much easier if you use declared properties rather than dereferencing class instance variables. For example, if name was declared as:
#property (nonatomic, copy) NSString *name; // you could use `retain`, too, but `copy` is safer when dealing with strings
Then you would set the name property as so:
RUBEImageInfo* imgInfo = [[[RUBEImageInfo alloc] init] autorelease];
NSString *nm = [NSString stringWithUTF8String:img->name.c_str()];
imgInfo.name = nm;
// this is no longer needed as the `name` setter will take care of memory semantics
// [imgInfo->name retain]; // I'm using it outside of this method
By using the setter accessor method (i.e. the "dot syntax" of imgInfo.name), it will take care of a lot of routine memory semantics of releasing any previous object that name may have referenced, and it will do the necessary copy or retain. Obviously, the RUBEImageInfo method dealloc still needs to release name, but at least it simplifies the memory semantics of the name property of RUBEImageInfo objects.
Since you are using manual reference counting, I'd encourage you to investigate the "static analyzer" (invoked by selecting "Analyze" from Xcode's "Product" menu). The Leaks tool in Instruments will tell you what leaked, but it doesn't tell you where the leak happened; it has no way of knowing; it can only show you where the leaked object was allocated and you'll have to hunt down the logic error yourself. The static analyzer can sometimes point out errors that lead to leaks, but more importantly, show you where the leak was caused, rather than just where the leaked object was originally instantiated. You should have a clean bill of health from the static analyzer before you even bother running Instruments.
Looking at your code sample, if you're not going to use declared properties (not sure why you wouldn't, as it makes life easier, but to each his own), I'd suggest making sure you initialize all of your objects in init and release all of them in dealloc:
#implementation RUBEImageInfo
-(id)init
{
if ((self=[super init])) {
body = NULL;
name = nil;
sprite = nil;
// I might initialize other class instance variables here, too, but that's up to you
}
return self;
}
-(void) dealloc {
[name release];
// shouldn't you release `body` and `sprite`, too?
[super dealloc];
}
#end
Then your code that sets the name instance variable would make sure to release the previous object before setting the new object. Thus the initial instantiation might look like:
RUBEImageInfo* imgInfo = [[[RUBEImageInfo alloc] init] autorelease];
NSString *nm = [NSString stringWithUTF8String:img->name.c_str()];
imgInfo->name = [nm retain]; // retain the new object
But if you update it later, you should:
NSString *nm = [NSString stringWithUTF8String:someNewImg->name.c_str()];
[imageInfo->name release]; // release the old one
imgInfo->name = [nm retain]; // retain the new object

How does adding a variable as a property affect it?

What difference does it make in memory management to define a variable as a property? For instance:
#interface foo {
NSString *theStr;
}
#end
#implementation foo
- (void)bar {
NSLog(theStr);
}
#end
Versus:
#interface foo {
NSString *theStr;
}
#property(retain) NSString *theStr;
#end
#implementation foo
#synthesize theStr;
- (void)bar {
NSLog(theStr);
}
#end
It seems like the first is autoreleased or something similar, while the second is retained throughout the life of the class. Is that the case, or what is the difference?
If you define a variable just in the interface without defining it as a property (as in your first example) means that you'll have to take care of everything related to memory management yourself. Assigning something to that variable will not retain it automatically, not will setting the variable to something else release the previous value.
Defining it as a property creates getter and setter methods under the hood. Most importantly, if you use it with the "retain" keyword, your setter method will retain the new value (and release the old one if there was one).
Note that the setter method will only be invoked if you use the dot notation, e.g., self.myStr = #"new string", or the method call, e.g., [self setMyStr:#"new string"]. If you just call myStr = #"new string" the setter method will not be called and you need to release the old value yourself and retain the new one.
I don't think the first case shows an autoreleased object, it would all depend on how you managed the creation and the destruction of that particular object. If for instance when you create that object you call:
//This string will indeed be autoreleased
theStr=[NSString stringWithString:#"Jibber jabber"];
//Or even
theStr=#"Jibber jabber";
But you have to take charge of the memory management if you create it in the following way:
//Manage my memory
theStr=[[NSString alloc] init];
//You have to release this property on the dealloc method
-(void)dealloc{
[theStr release];
[super dealloc];
}
On your second example, you create a setter and a getter method for the property theStr and by adding the nonatomic attribute, you make your property not thread safety, meaning that a thread can begin to modify your property while another one is already editing it. And by setting the retain attribute to your property, the setter method will be synthesized the following way:
- (void) setTheStr:(NSString *) newString {
[newString retain];
[theStr release];
theStr = newSupervisor;
}
You can consult more about this in one of my favorite books, Learning Objective-C 2.0 in chapter 12.

Is this a good (Cocoa-like, Apple-approved) model class?

I've been using Objective-C for a while, but I've not been following Apple's guidelines very well. Recently I read Cocoa Design Patterns and the Model Object Implementation Guide, and I'm trying to do some very simple things, but do them very well.
Have I missed any major concepts? Please don't mention self = [super init]; that's been covered many times on SO already. Feel free to critique my #pragma marks though!
#import "IRTileset.h"
#import "IRTileTemplate.h"
#interface IRTileset () //No longer lists protocols because of Felixyz
#property (retain) NSMutableArray* tileTemplates; //Added because of TechZen
#end
#pragma mark -
#implementation IRTileset
#pragma mark -
#pragma mark Initialization
- (IRTileset*)init
{
if (![super init])
{
return nil;
}
tileTemplates = [NSMutableArray new];
return self;
}
- (void)dealloc
{
[tileTemplates release];
[uniqueID release]; //Added because of Felixyz (and because OOPS. Gosh.)
[super dealloc]; //Moved from beginning to end because of Abizern
}
#pragma mark -
#pragma mark Copying/Archiving
- (IRTileset*)copyWithZone:(NSZone*)zone
{
IRTileset* copy = [IRTileset new];
[copy setTileTemplates:tileTemplates]; //No longer insertTileTemplates: because of Peter Hosey
[copy setUniqueID:uniqueID];
return copy; //No longer [copy autorelease] because of Jared P
}
- (void)encodeWithCoder:(NSCoder*)encoder
{
[encoder encodeObject:uniqueID forKey:#"uniqueID"];
[encoder encodeObject:tileTemplates forKey:#"tileTemplates"];
}
- (IRTileset*)initWithCoder:(NSCoder*)decoder
{
[self init];
[self setUniqueID:[decoder decodeObjectForKey:#"uniqueID"]];
[self setTileTemplates:[decoder decodeObjectForKey:#"tileTemplates"]]; //No longer insertTileTemplates: because of Peter Hosey
return self;
}
#pragma mark -
#pragma mark Public Accessors
#synthesize uniqueID;
#synthesize tileTemplates;
- (NSUInteger)countOfTileTemplates
{
return [tileTemplates count];
}
- (void)insertTileTemplates:(NSArray*)someTileTemplates atIndexes:(NSIndexSet*)indexes
{
[tileTemplates insertObjects:someTileTemplates atIndexes:indexes];
}
- (void)removeTileTemplatesAtIndexes:(NSIndexSet*)indexes
{
[tileTemplates removeObjectsAtIndexes:indexes];
}
//These are for later.
#pragma mark -
#pragma mark Private Accessors
#pragma mark -
#pragma mark Other
#end
(Edit: I've made the changes suggested so far and commented which answers discuss them, in case anyone needs to know why.)
Please don't mention self = [super init]…
So, why aren't you doing that?
The same goes for initWithCoder:: You should be using the object returned by [self init], not assuming that it initialized the initial object.
- (void)dealloc
{
[super dealloc];
[tileTemplates release];
}
As Abizern said in his comment, [super dealloc] should come last. Otherwise, you're accessing an instance variable of a deallocated object.
- (IRTileTemplate*)copyWithZone:(NSZone*)zone
The return type here should be id, matching the return type declared by the NSCopying protocol.
{
IRTileset* copy = [IRTileset new];
[copy insertTileTemplates:tileTemplates atIndexes:[NSIndexSet indexSetWithIndex:0]];
[copy setUniqueID:uniqueID];
You're inserting zero or more objects at one index. Create your index set with a range: location = 0, length = the count of the tileTemplates array. Better yet, just assign to the whole property value:
copy.tileTemplates = self.tileTemplates;
Or access the instance variables directly:
copy->tileTemplates = [tileTemplates copy];
(Note that you must do the copy yourself when bypassing property accessors, and that you are copying the array on behalf of the copy.)
return [copy autorelease];
}
copyWithZone: should not return an autoreleased object. According to the memory management rules, the caller of copy or copyWithZone: owns the copy, which means it is the caller's job to release it, not copyWithZone:'s.
#synthesize tileTemplates;
[et al]
You may want to implement the single-object array accessors as well:
- (void) insertObjectInTileTemplates:(IRTileTemplate *)template atIndex:(NSUInteger)idx;
- (void) removeObjectFromTileTemplatesAtIndex:(NSUInteger)idx;
This is optional, of course.
//However, should I list protocols
here, even though they're already
listed in IRTileset.h?
No, you shouldn't. The class extension declared in the implementation file is an extension, so you don't have to care about which protocols the class has been declared to follow.
I would recommend to mark your instance variables' names with an underscore: _tileTemplates. (Purists will tell you to affix rather than prefix the underscore; do that if you're afraid of them.)
Don't use new to instantiate classes. It's not recommended ever, as far as I understand.
[NSMutableArray new]; // :(
[NSMutableArray arrayWithCapacity:20]; // :)
Don't call [super dealloc] before doing your own deallocation! This can cause a crash in certain circumstances.
- (void)dealloc
{
[tileTemplates release];
[super dealloc]; // Do this last
}
I'm not sure what type uniqueID has, but shouldn't it also be released in dealloc?
I would never put my #synthesize directives in the middle of a file (place them immediately below ´#implementation´).
Also, having no clear idea about the role of this class, countOfTileTemplates doesn't sound good to me. Maybe just ´count´ will do, if it's unambiguous that what this class does it to hold tile templates?
It looks pretty good except you've left your properties open to arbitrary manipulation by external objects. Ideally, the data should be manipulated directly only by the model class itself and external objects should have access only via dedicated methods.
For example what if some external code calls this:
myIRTileset.tileTemplates=someArray;
Boom, you've lost all your data.
You should define both the data properties as readonly. Then write accessors internal to the class that will managed their retention within the class implementation. This way the only way for an external object to change the tileTemplates is by calling the - insertTileTemplates:atIndexes: and removeTileTemplatesAtIndexes: methods.
Edit01:
I think I mangled it the first go, so let me try again. You should setup you data model class in the following pattern:
Interface
#interface PrivateTest : NSObject {
#private
//iVar is invisible outside the class, even its subclasses
NSString *privateString;
#public
//iVar is visible and settable to every object.
NSString *publicString;
}
#property(nonatomic, retain) NSString *publicString; //property accessors are visible, settable and getable.
//These methods control logical operations on the private iVar.
- (void) setPrivateToPublic;
- (NSString *) returnPrivateString;
#end
So in use it would look like:
Implementation
#import "PrivateTest.h"
//private class extension category defines
// the propert setters and getters
// internal to the class
#interface PrivateTest ()
#property(nonatomic, retain) NSString *privateString;
#end
#implementation PrivateTest
//normal synthesize directives
#synthesize privateString;
#synthesize publicString;
// Methods that control access to private
- (void) setPrivateToPublic{
//Here we do a contrived validation test
if (self.privateString != nil) {
self.privateString=self.publicString;
}
}
- (NSString *) returnPrivateString{
return self.privateString;
}
#end
You would use it like so:
PrivateTest *pt=[[PrivateTest alloc] init];
// If you try to set private directly as in the next line
// the complier throws and error
//pt.privateString=#"Bob"; ==> "object cannot be set - either readonly property or no setter found"
pt.publicString=#"Steve";
[pt setPrivateToPublic];
NSLog(#"private=%#",[pt returnPrivateString]); //==> "Steve"
Now the class has bullet proof data integrity. Any object in your app can set and get the publicString string property but no external object can set or get the private.
This means you can safely allow access to the instance by any object in your app without worrying that a careless line of code in some minor object or method will trash everything.
two minor nitpicks:
One is the init method, (where stylistically I'm against having 2 different return points but thats just me), however there's nothing stopping super's init from returning a different object than itself or nil, eg a different object of its class or even just another object altogether. For this reason, self = [super init] is generally a good idea, even if it probably won't do much in practice.
Second is in the copyWithZone method, you don't copy the tileTemplates, which could be intentional but is generally a bad idea (unless they're immutable). Copying an object is supposed to have the same effect as allocing a fresh one, eg. retain count of 1, so don't autorelease it. Also, it doesn't look like you do anything with the zone, so you should probably replace it with something like
- (IRTileTemplate*)copyWithZone:(NSZone*)zone {
IRTileset* copy = [[IRTileset allocWithZone:zone] init];
[copy insertTileTemplates:[tileTemplates copyWithZone:zone]
atIndexes:[NSIndexSet indexSetWithIndex:0]];
[copy setUniqueID:uniqueID];
return copy;
}
thats everything I found; with the exception of the retain count of copy (which WILL lead to bugs later on) mostly just stuff that I prefer, you can do it your way if you like it better. Good work

Methods from #synthesize?

When you synthesize a property (see below)
#interface CelestialBody : NSObject {
NSString *name;
}
...
#interface Planet : NSObject {
NSString *name;
int mass;
CelestialBody *moon;
}
#property(nonatomic, retain) NSString *name;
#property(assign) int *mass;
#property(nonatomic, retain) CelestialBody *moon;
...
#implementation Planet
#synthesize name;
#synthesize mass;
#synthesize moon;
...
You get setters and getters for each of the iVars (i.e.)
[newPlanet setName:#"Jupiter"];
[newPlanet setMass:57];
NSString *closestName = [newPlanet name];
int largestMass = [newPlanet mass];
CelestialBody *newMoon = [[CelestialBody alloc] initWithName:#"Callisto"];
[self setMoon:newMoon];
[newMoon release];
but you also get the ability to release the object using ...
// Releases the object (frees memory) and sets the object pointer to nil.
[self setMoon: nil];
There will of course be deallocs for each Class.
// Moon
-(void)dealloc {
[name release];
[super dealloc];
}
// Planet
-(void)dealloc {
[name release];
[moon release];
[super dealloc];
}
Am I getting this right?
gary
Unless your planet object is declared as a property within some other class, using the retain/copy attributes, you can't release it this way.
When you declare a property using retain/copy, the resulting setter will release the old value and assign the new value, retaining or copying it in the process. If you pass nil, you will release the old value and assign nil, retaining or copying it, and retaining/copying nil is nil, so effectively you end up releasing the old value and assigning nil to the ivar.
This is an acceptable way to release instance variables.
In order to be able to release your newPlanet instance this way, you'd have to have declared it in a class as a property with either retain or copy.
As a further example, since your planet object declares its properties in this way, you could release those using this method.
Or in the Planet class's dealloc method, you could do:
self.name = nil;
This would release name and assign nil to it.
"you also get the ability to release the object"
Yes, as long as you didn't declare it with the assign attribute.
As you probably know, one of the reasons (although perhaps not the primary one) for using declared properties is that you can do:
self.moon = aMoon;
rather than;
[self setMoon:aMoon];
They are equivalent. That means that your deallocation can look like this:
self.moon = nil; // Releases and sets to nil
But remember to never just do:
moon = nil; // Sets to nil, doesn't release
It's very good practice to not only release the object, but to set the variable to nil, as you do, because otherwise some other code could mistakenly try to use the pointer that is left in the variable.
Your example shows the synthesis of one class's ivars (those of Planet) but the use of another (whatever "self" is). Is the "newPlanet" property of "self" in your last example also synthesized as (retain)? If so, then: Yes, setting newPlanet to nil will release whatever self's old "newPlanet" was.
I think you are not getting it right.
After your question update, yes, you can do that, and also:
self.moon = [[CelestialBody alloc] initWithName:#"Callisto"];
and release it later, probably in your dealloc method:
self.moon = nil;
Apple Objective-c 2.0 Properties and Memory Management docs are pretty good. Check Mac Dev Center library.