views:

113

answers:

1

Stupid question, but why do we need to use 'retain' when declaring a property? Doesn't it get retained anyway when it's assigned something?

Looking at this example, it seems that an object is automatically retained when alloc'ed, so what's the point?

#import "Fraction.h"
#import <stdio.h>

int main( int argc, const char *argv[] ) {
    Fraction *frac1 = [[Fraction alloc] init];
    Fraction *frac2 = [[Fraction alloc] init];

    // print current counts
    printf( "Fraction 1 retain count: %i\n", [frac1 retainCount] );
    printf( "Fraction 2 retain count: %i\n", [frac2 retainCount] );

    // increment them
    [frac1 retain]; // 2
    [frac1 retain]; // 3
    [frac2 retain]; // 2

    // print current counts
    printf( "Fraction 1 retain count: %i\n", [frac1 retainCount] );
    printf( "Fraction 2 retain count: %i\n", [frac2 retainCount] );

    // decrement
    [frac1 release]; // 2
    [frac2 release]; // 1

    // print current counts
    printf( "Fraction 1 retain count: %i\n", [frac1 retainCount] );
    printf( "Fraction 2 retain count: %i\n", [frac2 retainCount] );

    // release them until they dealloc themselves
    [frac1 release]; // 1
    [frac1 release]; // 0
    [frac2 release]; // 0

■output

Fraction 1 retain count: 1

Fraction 2 retain count: 1

Fraction 1 retain count: 3

Fraction 2 retain count: 2

Fraction 1 retain count: 2

Fraction 2 retain count: 1

Deallocing fraction

Deallocing fraction


This is driving me crazy!

A: 

The default behavior of a property is ASSIGN, not RETAIN. These are not the same behavior. If your property is for a primitive data type, such as an int, then ASSIGN would be correct to use. However, if you specify (retain) and the property points to an object pointer such as NSObject *object, the pointer is assigned the memory address of the object and it's retain count is incremented by one. If your program consists of nothing more than a lone main function, then it's purpose is hard to see. However, suppose your class had this method:

-(void)setMyArrayWithString:(NSString *)s{
    myArray = [NSArray arrayWithObject:s];
    }

Suppose myArray is defined as NSArray *myArray and has the proper @property (retain) statement. Everything works fine until the method returns. This is because the object returned from NSArray is an autoreleased object. If we don't retain it, it will be released by the NSAutoReleasePool and we won't be able to use it (and we'll get nasty bugs and bad access violations). To fix this we can do one of two things:

-(void)setMyArrayWithString:(NSString *)s{
    self.myArray = [NSArray arrayWithObject:s];
// OR
    myArray = [[NSArray arrayWithObject:s] retain];    
}

The first solution uses self.myArray to utilize the @property definition. This code assigns and then retains the object so that we don't lose it when the function returns. The second solution sets the NSArray *myArray pointer manually and then manually retains the NSArray object. Either way the NSAutoreleasePool will release the object at the end of the function, however it won't be deallocated because we still have our retain from earlier keeping it alive.

Steve Melvin
so an autoreleased object created in a method gets released when the method returns? i didn't know that.. i thought you had to autorelease it and then it hung around until you released the pool?
Adam
@AdamIt depends on what NSAutoreleasePool the object has been placed into. If you are creating a non-Cocoa app in which you explicitly create the NSAutoreleasePool, then when the pool is drained is up to you. However, if you are using a Cocoa framework such as AppKit or UIKit, when you create autorelease objects they are placed into a pool that is managed by Cocoa. Every now and then during the run-loop cocoa drains the pool. So if you are using a pool that you do not manage, such as cocoa's, retain objects that must survive after a method returns.
Steve Melvin
Ok makes sense- thanks!
Adam