[Objective C] 12 Dealloc

프로그래밍/iPhone Dev 2009. 11. 12. 17:37 Posted by galad
Dealloc
When your object contains other objects, you must free them whenever you yourself dealloc. One of the nice advantages to Objective-C is you can pass messages to nil, so there isn't a lot of error checking to release an object.
객체가 다른 객체를 갖고 있으면, 객체가 dealloc될 때 포함하고 있던 다른 것도 free 시켜야함. Objective-C의 좋은 장점은 메시지를 nil에게 보낼 수 있기에 객체 release 시에 많은 에러 체킹이 없다는 점이라는 군.

아래 소스는 원 소스를 살짝 변경해서 retain/release를 이용해 인스턴스 변수의 dealloc이 자동으로 일어나는 것을 잘 보여줌.
원 소스는 파일 첨부

Fraction.h
#import <Foundation/NSObject.h>

@interface Fraction: NSObject {
    int numerator;
    int denominator;
}

-(Fraction*) initWithNumerator: (int) n andDenominator: (int) d;
-(Fraction*) initWithNumerator: (int) n denominator: (int) d;
-(void) print;
-(void) setNumerator: (int) n;
-(void) setNumerator: (int) n andDenominator: (int) d;
-(void) setDenominator: (int) d;
-(int) numerator;
-(int) denominator;
@end


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

@implementation Fraction
-(Fraction*) initWithNumerator: (int) n andDenominator: (int) d {
    self = [super init];
   
    if(self) {
        [self setNumerator: n andDenominator: d];
    }
   
    return self;
}

-(Fraction*) initWithNumerator: (int) n denominator: (int) d {
    self = [super init];
   
    if(self) {
        [self setNumerator: n andDenominator: d];
    }
   
    return self;
}

-(void) print {
    printf( "%i/%i", numerator, denominator );
}

-(void) setNumerator: (int) n {
    numerator = n;
}

-(void) setNumerator: (int) n andDenominator: (int) d {
    numerator = n;
    denominator = d;
}

-(void) setDenominator: (int) d {
    denominator = d;
}

-(int) denominator {
    return denominator;
}

-(int) numerator {
    return numerator;
}

// dealloc
-(void) dealloc {
    printf( "Deallocing fraction : %i\n", numerator );
}
@end


AddressCard.h
#import "Fraction.h"
#import <Foundation/NSObject.h>

@interface AddressCard: NSObject {
    Fraction *first;
    Fraction *last;
    Fraction *email;
}

-(AddressCard*) initWithFirst: (Fraction*) f
                last: (Fraction*) l
                email: (Fraction*) e;
-(Fraction*) first;
-(Fraction*) last;
-(Fraction*) email;
-(void) setFirst: (Fraction*) f;
-(void) setLast: (Fraction*) l;
-(void) setEmail: (Fraction*) e;
-(void) setFirst: (Fraction*) f
        last: (Fraction*) l
        email: (Fraction*) e;
-(void) setFirst: (Fraction*) f last: (Fraction*) l;
-(void) print;
@end


AddressCard.m
#import "AddressCard.h"
#import <stdio.h>

@implementation AddressCard
-(AddressCard*) initWithFirst: (Fraction*) f
                last: (Fraction*) l
                email: (Fraction*) e {
    self = [super init];

    if ( self ) {
        [self setFirst: f last: l email: e];
    }

    return self;
}

-(Fraction*) first {
    return first;
}

-(Fraction*) last {
    return last;
}

-(Fraction*) email {
    return email;
}

-(void) setFirst: (Fraction*) f {
    [f retain]; // 새로 받는 것을 +1
    [first release]; // 기존 것을 -1. 이때 dealloc 됨
    first = f;
}

-(void) setLast: (Fraction*) l {
    [l retain];
    [last release];
    last = l;
}

-(void) setEmail: (Fraction*) e {
    [e retain];
    [email release];
    email = e;
}

-(void) setFirst: (Fraction*) f
        last: (Fraction*) l
        email: (Fraction*) e {
    [self setFirst: f];
    [self setLast: l];
    [self setEmail: e];
}

-(void) setFirst: (Fraction*) f last: (Fraction*) l {
    [self setFirst: f];
    [self setLast: l];
}

-(void) print {
    printf( "%i (%i) <%i>", [first numerator],
                                [last numerator],
                                [email numerator] );
}

// AddressCard가 dealloc될 때, 기존에 남아있던 클래스 변수들의 retain값을 모두 -1해서 dealloc 시킴
-(void) dealloc {
    [first release];
    [last release];
    [email release];

    [super dealloc];
}
@end


main.m
#import "AddressCard.h"
#import "Fraction.h"
#import <stdio.h>

int main( int argc, const char *argv[] ) {
    Fraction *first =[[Fraction alloc] initWithNumerator: 1 andDenominator: 10];
    Fraction *last = [[Fraction alloc] initWithNumerator: 2 andDenominator: 20];
    Fraction *email = [[Fraction alloc] initWithNumerator: 3 andDenominator: 30];
    AddressCard *tom = [[AddressCard alloc] initWithFirst: first
                                            last: last
                                            email: email];

    // we're done with the strings, so we must dealloc them
    // 사실 여기서 dealloc되지는 않고 retainCount가 -1됨.
    printf( "Retain count. Before release - first: %i\n", [first retainCount] ); // AddressCard 생성 시에 +1됨
    [first release];
    [last release];
    [email release];
    printf( "Retain count. After release  - first: %i\n", [first retainCount] );
    system("PAUSE");

    // print to show the retain count
    printf( "Retain count: %i\n", [[tom first] retainCount] );
    [tom print];
    printf( "\n" );
    system("PAUSE");
   
    Fraction *first2 =[[Fraction alloc] initWithNumerator: 5 andDenominator: 50];
    printf( "Set first again\n" );
    [tom setFirst: first2]; // setFirst할 때 first2를 retain. first2 = 2. 그리고 tom에 있는 기존 first를 -1해서 dealloc시킴.
   
    [first2 release]; // 위와 마찬가지로 사용하지 않으므로 must delloc. first2 = 1.
    // setFirst 시에 retain했으므로 release해도 아직 1. delloac되지는 않았음
   
    printf( "Retain count: %i\n", [[tom first] retainCount] );
    [tom print];
    printf( "\n" );
   
    // free memory
    [tom release];

    system("PAUSE");
    return 0;
}


결과
Retain count. Before release - first: 2
Retain count. After release  - first: 1
계속하려면 아무 키나 누르십시오 . . .
Retain count: 1
1 (2) <3>
계속하려면 아무 키나 누르십시오 . . .
Set first again
Deallocing fraction : 1
Retain count: 1
5 (2) <3>
Deallocing fraction : 5
Deallocing fraction : 2
Deallocing fraction : 3
계속하려면 아무 키나 누르십시오 . . .

- 위의 Deallocing fraction : X 메시지에서 보다시피 setXXX 하면서 retain/release 하는 것과 dealloc될 때 release 하는 것으로
포함하고 있는 객체를 자동으로 dealloc 시킬 수 있음.
- This example shows not only how to make a dealloc method, as shown in AddressCard.m, but one way to do member variables.
 dealloc 메소드 사용법 뿐 아니라, 멤버 변수 취급 방식에 대한 예제임.
- The order of the 3 operations in each set method is very important. Lets say you'return passing a parameter of yourself to one of your methods (a bit of an odd example, but this can happen). If you release first, THEN retain you will destruct yourself! That's why you should always 1) retain 2) release 3) set the value.
 set 시에는 반드시 매개변수 retain 후에, 멤버변수 release 할 것.
 만약 넘겨 받은 매개변수를 return하는 경우에 release를 먼저하면 그 때 매개변수가 dealloc되어버린다.
 위의 예제에선 상관없으나 습관의 문제일 듯. 로직상으로도 먼저 retain하는 것이 옳고...
- Normally one wouldn't initialize variables with C strings because they don't support unicode. The next example, with NSAutoreleasePool shows the proper way to do strings and initializing.
- This is just one way of handling member variable memory management. One way to handle this is to create copies inside your set methods.
  이건 멤버변수 메모리 관리의 한 방법. 다른 방법은 set 메소드에서 카피를 생성하는 방법.

'프로그래밍 > iPhone Dev' 카테고리의 다른 글

[Objective C] 14 NSArray  (0) 2009.11.13
[Objective C] 13 Autorelease Pool  (1) 2009.11.13
[Objective C] 11 Retain and Release  (0) 2009.11.12
[Objective C] 10 Protocols  (0) 2009.11.11
[Objective C] 09 Posing  (0) 2009.11.10