So, now that we've written and used getters and setters and looked at property notation, let's look at a few related items.
Why do we have to specify @synthesize propertyName;? Why doesn't the compiler just generate it for us automatically?
There are two good answers to this.
First, the compiler may not know where to generate the code. It's possible that the implementation of your class is split across several source files. If you write
@synthesize propertyName; into one of them, the compiler knows that now's the time. (Of course, if you wrote the same line into more than one file, you'd generate multiple functions with the same name, and Xcode would probably start generating little red marks.) It's possible, of course, that the Objective C gurus could have figured out a way to handle this if this was the only issue.
Second, and perhaps more importantly, maybe you want to use the property "dotted" notation, but the code that the compiler would automatically generate won't cut it for you. Suppose that we wanted to link "text" and "value" so that any time you set "value" it would change "text" to match, but you also wanted to use the "dot" notation to access either. Thus, you want the setter for "value" to look something like this:
Code:
-(void) setValue:(int)intValue
{
value = intValue;
self.text = [NSString stringWithFormat:@"%d", intValue];
}
Now, there's no way that Objective C can read your mind (at least in ObjC 2.0 - you may have to wait for 3.0 for this feature). Thus, the gurus give you another option: you can declare the "value" property exactly the same way, but provide your own getters and setters. You do this by giving Objective C
@dynamic propertyName; instead of
@synthesize propertyName;
"@dynamic" basically tells Objective C that you want it to translate the "object.property" notation into calls to "[obj property]" and "[obj setProperty:x]" the same way it did, but that you will provide the implementation for "property" and "setProperty:" instead of having them automatically generated.
Every now and then being able to do this is convenient.
What's this autorelease stuff?
Remember rule #1 from the previous part - if you allocated it, you're responsible for releasing it. There's a problem, however. Sometimes you need to create something and return it so that someone else can use it. You can't do:
Code:
- (MyObject *)getObject
{
MyObject *returnValue = [[MyObject alloc] init];
[returnValue release];
return returnValue;
}
After the first line, the object has a retain count of one. When you release it in the second line, the retain count drops to zero, so it will get deallocated before you ever get a chance to return it.
But you also can't do
Code:
- (MyObject *)getObject
{
return [[MyObject alloc] init];
}
because you've broken rule # 1 - you allocated it, but you didn't release it. What you really need to be able to do is tell Objective C "I'm responsible for this object, so I want to release it, but I want the release to be delayed until whoever asked for this object has had a chance to use it."
This is the function of the autorelease pool.
The correct way (or at least one correct way) to write the function above is:
Code:
- (MyObject *)getObject
{
return [[[MyObject alloc] init] autorelease];
}
What happens is that you allocate and init the item, and then call "autorelease" on it. The system reacts to this by making an entry in a "deferred release" pool or, more correctly, in the "autorelease pool." This entry guarantees that the pool will call "release" on the object at a later date, thus satisfying your responsibility. In the meantime, however, the code that called this function can:
1) Use the object (because it hasn't
actually been released for the final time yet) and then just forget about it. The calling code didn't allocate the object, so it's not responsible for releasing it (rule #1).
2) Retain the object. In this case, the retain count gets incremented, so when the automatic "release" happens later, the count will only get decremented to one, and the object will hang around. This makes the caller responsible for the ultimate dealloc-ing "release," based on Rule #2.
So when does the object actually get released? In the iPhone implementation, an autorelease pool is created for each message that is processed by the system and the objects in the pool are all automatically released after processing on that message has completed. Thus, when your code returns back to the operating system, any autorelease objects that haven't been otherwise retained will go poof at that point. If you want them to hang around any longer than that, you need to retain them yourself.
If you remember our earlier example
Code:
-(void) setValue:(int)intValue
{
value = intValue;
self.text = [NSString stringWithFormat:@"%d", intValue];
}
in this case, the string object that
stringWithFormat: returns has been autoreleased before being returned to us. By assigning it to "self.text", however, the "text" setter retains it. As a result, the value will stick around. If we had done something like this, however
Code:
-(void) logValue:(int)intValue
{
NSLog(@"%@", [NSString stringWithFormat:@"%d", intValue]);
}
then the we would be using the autoreleased string and then letting the autorelease pool clean up after
stringWithFormat: - the string object that it returns would be deallocated some time after our function returns.
Now, we talked earlier about writing code like this:
Code:
-(void)doSomething
{
SomeObject *obj = [[SomeObject alloc] init];
self.obj = obj;
[obj release];
}
The presence of the autorelease pool means that it is equally valid to write this code like this:
Code:
-(void)doSomething
{
SomeObject *obj = [[[SomeObject alloc] init] autorelease];
self.obj = obj;
}
In the first case, the "alloc" on line 1 was balanced out by the "release" on line 3. In the second case, the "alloc" was balanced out by the (deferred) release from the "autorelease".
So which is better? While either is technically correct, Apple indicates that the second form is slightly less efficient than the first - there's overhead associated in getting hold of the current autorelease pool, making an entry in it and then later having the pool do the release. In the case of
Code:
- (MyObject *)getObject
{
return [[[MyObject alloc] init] autorelease];
}
we really don't have any choice but to suffer that overhead, but in the case where we have both ends of the deal, it doesn't hurt to use the slightly more efficient way of doing things. (And it's just about the same number of keystrokes - maybe just a few more.)