블로그 이미지
내게 능력 주시는 자 안에서 내가 모든것을 할수 있느니라 - 빌립보서 4 : 13 - happydong

카테고리

Happydong (1363)
프로그래밍 (156)
MUSIC (16)
인물 (3)
Utility (10)
세미나 소식&내용 (22)
IT뉴스 (18)
운동 (830)
CAFE (10)
Life (282)
Total
Today
Yesterday

'Property'에 해당되는 글 1건

  1. 2010.08.12 [펌] [문씨의 강좌] 메모리 관리2 <Property>



원문 : http://cafe.naver.com/mcbugi/71571

출처 : 맥부기 아이폰 카페




안녕하세요 문씨입니다

앞서 다룬 메모리 관리 강좌에서 부족한 부분인

@Property부분을 집중적으로 다루어 보겠습니다.


 프로퍼티는 외부에서 객체에 접근하기 위해 선언해서 정의해 주는 것입니다.

사실 프로퍼티가 없어도 문제는 없습니다.

그냥 setter, getter함수를 만들어 놓으면 되기 때문이죠


실제로 Foundation에 있는 NS계열 클래스 들을 보면

작성된지 오래된 클래스의 경우 프로퍼티 선언 없이 함수만으로 정의해 놓기도 했습니다.



그럼 프로퍼티에 대해 다루기 전에 기초로 돌아가 보겠습니다.

클래스가 하나 있습니다
@interface object : NSObject {
        NSString *testString;
}
@end

내부에는 전역 변수로 문자열 객체(testString)를 가지고 있습니다.


이것을 외부에서 접근 하려면 어떻게 해야 할까요?


보통 프로그래밍 수업을 들으면 자주 듣는 setter와 getter입니다.


그래서 구현을 해보면

@interface object : NSObject {
        NSString *testString;
}
- (NSString *)testString;
- (void)setTestString:(NSString *)newString;
@end

이런 식으로 읽는용(getter)과 쓰는용(setter)함수를 만들게 됩니다.


그리고 본문에서는

@implementation object 
- (NSString *)testString {
        return testString;
}
- (void)setTestString:(NSString *)newString {
        [testString release];
        testString = [newString retain];
}
@end

이런식으로 구현합니다.
getter인 testString함수는 내부의 testString객체를 돌려줍니다.

여기서 함수명인 testString은 그저 함수명이고  전역변수 testString은 그저 변수일 뿐입니다.

명칭이 같아서 혼란이 있을수 있습니다.
이 부분에 대해서는 잠시뒤에 다루고

먼저 setter인 setTestString에서 보면
기존의 testString객체는 release처리해서 더이상 안쓴다고 알리고
새로운 객체에 retain걸어서 지금부터 쓴다고 알리고 객체의 주소값을 저장합니다.


이런식으로 받은 객체의 리테인 카운트는 책임 진다는 규칙을 따르고 있습니다.


다만 지금 위의 클래스는 아직 문제가 있죠

위 객체가 해제될때 처리가 안되어 있군요
@implementation object 
- (NSString *)testString {
        return testString;
}
- (void)setTestString:(NSString *)newString {
        [testString release];
        testString = [newString retain];
}
- (void)dealloc {
       [testString release];
       testString = nil;
       [super dealloc];
}
@end

이제 됬습니다.
객체가 해제될때 호출되는 dealloc함수에서 전역으로 가지고 있던 모든 객체들을 깔끔히 해제 처리해줍니다.

이것으로 메모리 관리는 잘된 객체가 완성되었습니다.

근데 코드가 좀 많네요

조금만 줄여볼까요

@implementation object 
- (NSString *)testString {
        return testString;
}
- (void)setTestString:(NSString *)newString {
        [testString release];
        testString = [newString retain];
}
- (void)dealloc {
       [self setTestString: nil];
       [super dealloc];
}
@end

이러면 setTestString함수를 호출하면서 기존의 객체는 해제 시키고 nil값이 저장됩니다.
위에서 일일이 release쓰고 nil처리 해주는 작업을 한줄로 처리하는것이죠


그럼 외부에서는 어떻게 사용할까요?

object *test = [[[object alloc] init] autorelease];
객체를 생성합니다.

NSLog(@”%@”, [test testString]);
이러면 testString함수를 호출해서 내부 전역변수(testString)의 데이터를 가져와서 로그로 찍어볼수 있게됩니다.


그리고 이번엔 
[test setTestString: @”I am your father”];
지정해봤습니다.


이걸로 클래스의 setter와 getter가 구현되었습니다.


근데 이런식으로 구현하다보면 대부분 같은 작업인데 변수만 일일이 만들고 지정하는 것도 고욕이겠군요

무엇보다 외부에서 접근하기 위한 변수 한개당 함수 두개씩 해서 최소 
6줄 정도의 코드가 x 변수수 = 코드량 만큼 고생합니다.

다르거 개발하기도 바쁜데 setter, getter하느라 시간 다보내겠군요
누구나 그렇겠지만 귀찬은건 질색입니다.

Objective-C를 개발하는 개발자들도 같은 생각을 했을겁니다.

지금 구현해본 클래스를 보면
@interface object : NSObject {
        NSString *testString;
}
- (NSString *)testString;
- (void)setTestString:(NSString *)newString;
@end


@implementation object 
- (NSString *)testString {
        return testString;
}
- (void)setTestString:(NSString *)newString {
        [testString release];
        testString = [newString retain];
}
- (void)dealloc {
       [self setTestString: nil];
       [super dealloc];
}
@end


헤더에서 한가지를 위해 변수, setter, getter 세줄이 소모되고,
본문에서는 7줄이 소모됩니다. (상황에 따라 6줄일수도 있음)

이것들을 각각 헤더에서 1줄씩 총 두줄로 줄여버릴수가 있습니다.


어떻게 할까요?




이렇게요?
@interface object : NSObject {
        NSString *testString;
}
- (NSString *)testString; - (void)setTestString:(NSString *)newString;
@end


@implementation object 
- (NSString *)testString { return testString; }
- (void)setTestString:(NSString *)newString { [testString release]; testString = [newString retain]; }
- (void)dealloc {
       [self setTestString: nil];
       [super dealloc];
}
@end


이래도 많네요 
단순히 줄만 줄여 보려는것이 아닌 문자도 줄여보려는 것입니다.


그래서 설계된것이 Property입니다.
프로퍼티 기능을 사용한 코드로 바꾸어 볼까요?

@interface object : NSObject {
        
}
@property (nonatomic, retain) NSString *testString;
@end

@implementation object 

@synthesize testString;

- (void)dealloc {
       [self setTestString: nil];
       [super dealloc];
}
@end

어떤가요?
매우 간단해 졌죠 

질문이 많겠지만 일단 헤더부터 봅시다

@property

프로퍼티는 말 그대로 속성이라는 뜻입니다.

그리고 헤더에서 @property는 단순히
이 클래스에는 정의한 이름의 (testString) 속성이 있다고 알리는 역활일 뿐입니다.

변수를 만든다거나 어떠한 작업도 없습니다.

그리고 프로퍼티에 달린 조건에 따라 이 속성이 어떤것인이 알릴수 있습니다.

문법을 볼까요

먼저 프로퍼티라고 선언합니다
@property

그리고 ( 를 열고 이 프로퍼티의 설정을 합니다 물론 이 부분은 빠질수도 있습니다.
@property (

atomic OR nonatomic
이 두 속성중 하나를 선택하는 것으로 기본값은 atomic입니다. 이부분은 멀티스레딩에 관련된 부분으로 보통 nonatomic을 사용합니다. 자세한건 개발자 문서 참고
assign OR retain OR copy
setter에서 객체를 지정 받을때 
assign의 경우 주소값만 지정받고
retain의 경우 기존것을 release후 새로 받은걸 retain합니다.
copy의 경우 기존것을 release후 새로 받은걸 copy합니다.
이부분은 setter에 관련있고 getter와는 관련 없습니다.
readonly OR 없음
readonly설정되면 setter가 없습니다. 말그대로 읽기 전용이죠


그외에 몇가지 더 속성이 있지만 개발자 문서(영어)를 참고하세요


그래서 nonatomic에 retain으로 하고 readonly는 안한다면
@property (nonatomic, retain) 
그리고  )를 닫습니다.

그리고 객체 종류를 선언합니다
@property (nonatomic, retain) NSString

그리고 마지막으로 외부 접근 변수명을 지정합니다 
마치 변수 선언하듯요
@property (nonatomic, retain) NSString *testString;


여기까지 선언했지만 한가지 주의할 점은 이 시점에서는
프로퍼티로 선언한 testString은 클래스 내부의 전역 변수와 아무런 관련이 없다는 것입니다.

그러면 헤더에서 작업은 끝났습니다.

프로퍼티로 NSString타입의 testString이 있다고 알렸을 뿐입니다.




이제 본문을 보죠.
@synthesize testString;

@synthesize문으로 헤더에서 정의한것을 실체화 하는겁니다.

이 @synthesize문은 참 많은것을 합니다.

간단히 
@synthesize testString;이라 설정해주면

세가지 작업을 합니다.

1. 먼저 같은 이름으로 내부에 전역 변수를 만듭니다.
즉 testString이라는 변수를 만드는것이죠

단 이미 헤더에 전역 변수로 같은 이름이 선언 되어 있으면 그대로 그것을 사용합니다.

getter함수를 만듭니다.
선언된 이름과 같은 이름의 함수를 만들고 돌려주는 변수도 같은 이름이 됩니다.

readonly설정이 되어 있지 않다면 setter함수를 만듭니다.

눈에 보이지는 않지만 @synthesize문 하나로 일일이 해줘야 하는 귀찬은 작업들을 알아서 해주는겁니다.



그리고 프로퍼티로 선언된것은 함수로서 인식하기도 하지만
점 문법으로 인식하기도 합니다.
test.testString 이런식으로요


위에서 사용해봤던
NSLog(@”%@”, [test testString]);
[test setTestString: @”I am your father”];

이부분은 여전히 이대로 사용할수도 있지만

Objective-C개발팀도 []가 많아지는건 원치 않았나 봅니다

그래서 지원되는 점 문법으로

NSLog(@”%@”, test.testString);
test.testString = @”I am your father”;
이렇게 알아보기 편하게 되었습니다.

확실히 알고 있어야 하는것은 이렇게 쓰기 편하게 되었지만 결국은 함수라는 것입니다.

점 문법은 함수명을 점(.)을 사용해도 인식해주는 기능입니다.

어떤분은 release보낼때 보통
[object release];
하는데

object.release;

하는 분도 봤습니다..
개발 스타일이야 각자 몫이지만 어떻게 사용하든 확실히 알고 사용해야 합니다.


실제로 
test.testString = @”I am your father”;
이렇게 써놓아도

컴파일러에서는 
[test setTestString: @”I am your father”];
이렇게 인식합니다.

그래서 readonly걸려있으면 함수를 찾을수 없다는 에러를 띄웁니다
변수를 찾을수 없다곤 안합니다.





그러면 애플이 잘쓰는 겉과 속이 다는 기법을 다루어 보겠습니다.


외부에서는 testString으로 접근하지만 내부는 전혀 다른 변수명을 사용하는 경우입니다.


아이폰 앱 좀 개발해 보셨으면 많이들 써봤을 겁니다.

UIViewController

거기서 특히 title속성

헤더를 보면 
@property(nonatomic,copy) NSString *title;  
라고 선언해두었습니다.

근데 헤더를 잘보면 어디에도 NSString *title이라는 전역 변수는 안보입니다.
대신
NSString         *_title;
이라는 변수가 선언 되어 있죠


그냥 딱 봐도 프로퍼티와 연결되어 있다는 걸 알수 있습니다.


어떻게 했는지 보겠습니다

@interface object : NSObject {
        
}
@property (nonatomic, retain) NSString *testString;
@end
일단 헤더에 다른 전역 변수를 만듭니다
@interface object : NSObject {
        NSString *_TESTString;
}
@property (nonatomic, retain) NSString *testString;
@end

그리고 본문에서는
@implementation object 

@synthesize testString = _TESTString;

- (void)dealloc {
       [self setTestString: nil];
       [super dealloc];
}
@end


이렇게 하면 외부에서는 testString으로 접근하며 내부에서는 실제로 _TESTString객체를 사용하게되는겁니다.


그리고 해외 강좌 중에서는
@interface object : NSObject {
        NSString *_testString;
}
@property (nonatomic, retain) NSString *testString;
@end

@implementation object 

@synthesize testString = _testString;

- (void)dealloc {
       [self setTestString: nil];
       [super dealloc];
}
@end

이렇게 사용하기도 합니다만

앞서 언급했다시피
코딩하는 사람의 스타일 문제이고

이런식으로 하는것은 단순히 프로퍼티 명과 실제 변수명을 다르게 하여
혼동을 방지하기 위한 것입니다.





@synthesize를 사용하지 않는 방법도 있습니다.


프로퍼티 선언만 해두고 직접 함수를 구현하는겁니다.

@interface object : NSObject {
        NSString *_testString;
}
@property (nonatomic, retain) NSString *testString;
@end

@implementation object 

- (NSString *)testString {
       return _testString;
}
- (void)setTestString:(NSString *)newString {
       [_testString release];
       _testString = [newString retain];
}

- (void)dealloc {
       [self setTestString: nil];
       [super dealloc];
}
@end

이러면 복잡해 지기도 합니다만
이렇게 밖에 할수 없는 상황도 있습니다.

예를 들어 testString에 접근할때 아무것도 없을때 기본적으로 값이 있어야하는경우
- (NSString *)testString {
       if (_testString == nil) {
           _testString = [[NSString alloc] initWithString:@”my eye, my eye”];
       }
       return _testString;
}

이런 기법은 UIViewController가 view속성에 처음 접근할때 뷰가 자동 생성되는 방식과 동일하다고 할수있습니다.



그리고 @synthesize설정을 하고도 함수를 구현할수도 있습니다.
함수가 없으면 자동으로 만들고 있으면 그것을 사용하도록 하는것이죠
@interface object : NSObject {
        NSString *_testString;
}
@property (nonatomic, retain) NSString *testString;
@end

@implementation object 

@synthesize testString = _testString;
- (NSString *)testString {
       if (_testString == nil) {
           _testString = [[NSString alloc] initWithString:@”my eye, my eye”];
       }
       return _testString;
}

- (void)dealloc {
       [self setTestString: nil];
       [super dealloc];
}
@end



readonly의 경우 setter가 없기 때문에 전역 변수를 직접 해제 처리하는 수밖에 없습니다.



마지막으로

외부에서는 프로퍼티로 접근할수밖에 없지만
내부에서는 프로퍼티와 전역 변수가 존재하기 때문에

확실히 이해하고 사용하지 않으면 

이런 사태도 벌어집니다.

프로퍼티로 선언했으니 nil처리만 하면 되겠지 하는 분도 봤습니다.
- (void)dealloc {
      _testString = nil; X
       [super dealloc];
}

_testString = nil;
이것과
self.testString = nil;
이것은 다른겁니다.

혼동하지 마세요

전역 변수는 직접 release후 nil처리
[_testString release]; _testString = nil;

프로퍼티는 setter로 nil
self.testString = nil;
Posted by happydong
, |