2010.08.08 20:07
문씨(swmoon203)
카페스탭
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;
Moon_iPhone_Study
작성자의 카페글 더보기
문씨님의 블로그
덧글 36개 |
등록순
| 조회수 2757 | 추천 4
담기 (+29) | 인쇄 | 신고
천상을달리다
2010/08/08 21:27답글|신고
오오오.. 1탄에 이어서.. 2탄 이번에도 첫번째 댓글달고 기다릴께요 ㅎㅎ ^^
레버
2010/08/08 22:14답글|신고
오오 2탄도 기대... 밤새 기다리겠단 소리 못하겠네요 이번엔 ㅎㅎ
문씨
2010/08/08 22:40신고
밤새서 쓸 생각은 없지만...
빠지면 저도 장담 못함 -_-;;;
아이맨
2010/08/08 22:15답글|신고
오 3탄도 기대한다고 말을 못하겠네요 ㅎㅎ
블루크리스탈
2010/08/08 23:32답글|신고
전 1탄 읽고 머리에 열 좀 나서 식히고 있습니다 ㅎ
책 튜토리얼 보면서 따라하다 나중에 또 와야겠습니다 ^^
문씨
2010/08/09 00:49답글|신고
완성...
종피
2010/08/09 00:58답글|신고
짱.
클린트
2010/08/09 01:06답글|신고
testString = _testString; nil처리 등등 이해없이 당연히 이렇게 쓰는 건가 하면서 쓰던 것들, 부족하게 알던 부분들에 대해 이해하는데 큰 도움(좀더 명확)이 되는 것 같습니다. 감사합니다.~~(꾸벅)
와이씨
2010/08/09 01:18답글|신고
그동안 무심코 습관 or 직관적으로 코딩하던 부분들 원리까지 알기 쉽게 너무 자세하게 써주셔서 감사드립니다~
항상 잘 보고 있습니다.
블루크리스탈
2010/08/09 02:10답글|신고
쭈욱 읽어 보긴 했는데 마지막 부분에서 약간 어렵네요 ㅎ
그래도 설명을 잘 해 주셔서 도움이 되었습니다
공부하다가 다시 읽어 봐야겠어요. ^^
정리 감사합니다.
맥초짜
2010/08/09 14:35답글|신고
와우~~~ 정리를 잘 해주셨네요... 감사합니다...
TigerKim
2010/08/09 15:15답글|신고
강의 잘 보았습니다. 감사드립니다. (__)
대쉬
2010/08/09 15:35답글|신고
아 이런부분은 잘몰랐네요 감사합니다
crono
2010/08/10 17:55답글|신고
감사드려요 ~~ 큰 도움이 됩니다 ~ ~
랑유
2010/08/11 00:40답글|신고
우와 감사합니다. 그동안 엄청 혼동하고 있었습니다. ;;;
문씨님은 항상 엑기스 중의 엑기스만 뽑아주시네요+_+
rokakjy
2010/08/11 13:16답글|신고
좋은정보 정말 감사감사^^
안드레
2010/08/11 17:28답글|신고
한줄한줄 읽을때마다 '아~~' 를 연발하게 되네요 좋은 정보 감사드립니다.
happydong
2010/08/12 16:50답글|신고
좋은 정보 정말 감사합니다^^
Objective-C 공부하는데 도움이 되네요~^^
조심히 담아 갈게요~~~
Tesla
2010/08/13 10:28답글|신고
이번에도 잘 읽었습니다...^^
항상 감사합니다.
adult1220
2010/08/13 18:04답글|신고
잘 읽었어요..
백과사전
2010/08/13 22:21답글|신고
너무 좋습니다 ㅎㅎ
군감자
2010/08/15 15:59답글|신고
잘 읽었읍니다. 좀더 공부해서 다시 한번 볼게요.ㅎ
taxhon
2010/08/16 09:29답글|신고
강의 잘 보았습니다~ 담아갈께요~^^
만능이자
2010/08/17 13:56답글|신고
큰 도움이 되었습니다. 감사합니다.
지돌스타
2010/08/17 18:23답글|신고
감사합니다. 오브젝티브C하면서 마지막 지적한 부분이 항상 헷갈리더군요.
드라이브
2010/08/18 13:28답글|신고
감사합니다~~~
seed
2010/08/18 19:43답글|신고
좋은 글 감사합니다.
제 티스토리에 퍼가요...^^
쭌
2010/08/20 14:20답글|신고
멋진 강좌 감사드립니다.
베메스
2010/08/23 16:44답글|신고
감사합니다.
믹스
2010/08/26 17:37답글|신고
우왕... 요즘 제가 계속 헷갈려 했던 부분이네요..
이것 때문에 컨트롤을 제외한 내부 변수는 property 를 사용안했었는데..
감사합니다.
Kisin 입
2010/08/30 20:33답글|신고
앗앗~! 감사합니다. ^^
코드라이더
2010/09/01 16:40답글|신고
좋은 자료 감사해요
이프로
2010/09/05 22:32답글|신고
감사드린다는말밖에.. ^^
개념정리 잘하고 갑니다.
버헤미안
2010/09/07 20:29답글|신고
와 정말 아무렇지 않게 사용하던 부분이 이런 의미가 있을 줄이야.. 저는 text.length와 [text length]가 무엇이 다른지도 이해가 되지 않았는데, 결국 .length가 변수가 아닌 프로퍼티 함수라는 거군요 'ㅡ' 이렇게 이해하는게 맞는지는 아직 확신이 없네요 ㅎ;
문씨
2010/09/07 20:41신고
결론은 .length는 length]함수를 호출하는것과 같다는 겁니다
그렇다면 거꾸로 속성 선언이 없을때도 호출이 가능하기도 합니다
예) [text release]; -> test.release;
덩크훈
2010/10/31 18:11답글|신고
와 정말 이해 쉽게 설명해주셨네요~
잘 배웠습니다^^
댓글 없음:
댓글 쓰기