본문 바로가기
모바일개발(Mobile Dev)/IOS개발(ObjectC)

xcode property 에 대해서

by 테크한스 2015. 5. 19.
반응형

1. 프로퍼티에 대해서

Objective-C의 프로퍼티가 있기 이전에는 getter/setter가 있었다. getter/setter는 자바와 같은 OOP언어에서 자주 쓰이는데 내부 변수 ( 멤버 변수 )를 외부에서 바로 값을 읽거나 변경하는 것을 피하기 위한 기법이다. 변수를 함수로 감싸서 읽거나 값을 변경할때 값을 검증하는 등의 일을 한다.

getter/setter를 만드는 일을 정말 귀찮은 일이다. 비슷한 코드를 반복해서 써야 하고 코드가 길어져서 가독성도 떨어지게 한다.

이 때문에 앱플에서는 2006년에 Objective-C 2.0을 발표하면서 이 부분을 개선하였다. 이른바 프로퍼티를 도입한 것이다. Objective-C 2.0에는 @property, @synthesize 키워드가 포함되었다. 이 키워드를 이용해서 코드를 작성하면 컴파일러가 getter/setter를 자동으로 생성 시켜준다.

프로퍼티는 단지 getter/setter를 쉽게 선언해주는 것만이 아니였다. 호출하는 방식도 변경되었다. 지금은 자연스러운 일이지면 처음 이 기술이 등장을 했을때는 그야말로 새로운 기술이였다.

그럼 프로퍼티의 사용방법에 대해서 알아보고 프로퍼티의 원리 및 정의 방법에 대해서 하나 하나 자세히 알아보도록 하자.



2. 프로퍼티의 원리

프로퍼티는 getter/setter를 작성하는 귀찮은 일을 개선하기 위해서 등장했다. (꼭 이 이유때문은 아니지만.. ) 이 세상의 그냥 되는 일은 없다. 따라서 누군가는 이 일(getter/setter)을 해주어야 한다. 이 일을 시킬 만만한 상대는 컴파일러다. 컴파일러에게 코드를 대신 작성하도록 하는 것이다. 그럼 컴파일러가 해주는 일을 알아보자..

// 헤더에 이렇게 정의한다.(xxx.h)
@property (attributes) type name;

// 소스에 정의(xxx.m) 
@synthesize name;   

그럼 헤더부터 알아보자.

속성(attribtues)에 따라서 다르지만 기본 속성이라고 가정하고 설명한다.

@property NSString *value;

은 다음과 같은 코드로 대치된다.

- (NSString)value;
- (void)setValue:(NSString
)newValue;

Objective-C의 관례처럼 getter는 같은 이름이고 setter만 setXXXX로 변형됩니다.

그리고 소스에 정의한

@synthesize value = _value;

는 컴파일러에 의해서 다음과 같은 코드로 변경됩니다. _value는 코드 생성시에 내부 변수로 사용된 변수 이름입니다.

 - (NSString*) value    
 {   
     @synchronized(self) {   
     return [[_value retain] autorelease];   
     }   
 }   

 - (void) setValue:(NSString*)aValue
 {
     @synchronized(self) {
        if( _value != aValue ) {
         [aValue retain];
         [_value release];
            _value = aValue;
        }
      }
 }

간단하다. property가 없던 시전에 직접 만들어 쓰런 getter/setter를 이제는 간단히 키워드로 정의만 해주며 이렇게 간단히 만들어 준다. 좋은 세상이 되었다.

여기서 생성되는 코드는 @property 를 사용할때 attribute에 따라서 달라지게 됩니다.


3. 프로퍼티 옵션들

앞에서도 언급한 것처럼 프로퍼티는 @interface와 @end 사이에 다음과 정의합니다.

@property (attributes) type name;

attributes에 따라서 내부 생성되는 코드들이 달라지기 때문에 원하는 용도에 따라서 적절히 사용해야 합니다.


3.1 메소드 이름 변경

프로퍼티는 내부적으로 메소드를 정의하게 된다. 이때 만들어지는 메소드의 이름은 관례에 따라서 getter는 프로퍼티이름과 동일 setter는 set을 붙이고 프로퍼티의 앞글자만 대문자로 만들어 사용한다.

즉,

@property BOOL exists;

--> 변환

// getter는 이름이 같고
- (BOOL) exists;
// setter는 set이 붙고 첫자면 대문자로
- (void) setExists;

하지만 경우에 따라서 다른 이름을 사용하고 싶을 때가 있다. 그럴때 다음 옵션을 사용한다.

getter
getter 함수 이름 지정
setter
setter 함수 이름 지정

다음처럼 사용한다.

@property (getter=isExists, setter=newSetExists) BOOL exists;

--> 변환

// getter는 이름이 같고
- (BOOL) isExists;
// setter는 set이 붙고 첫자면 대문자로
- (void) newSetExists;

보통 BOOL 타입 프로퍼티는 getter를 isXXX와 같이 is를 붙여 사용한다. 만약 나름의 코딩 스타일이 있다면 이 속성을 사용해서 변경이 가능하니 알아 두도록 하는 것이 좋을 것이다.


3.2 쓰기 여부

프로퍼티가 읽기만 허용해야 한다면 어떻게 할까? setter 함수만 만들어지지 않도록 하면 된다. 다음 옵션을 사용하면 setter 함수를 생성할지 말지를 결정할 수 있다.

readwrite
setter/getter 모두 생성 ( default )
readonly
getter만 생성

3.3 Setter 함수를 위한 속성들

Setter는 외부의 값을 저장하는 메소드이다. Objective-C는 레퍼런스 카운트에 따라서 객체의 라이프 사이클을 결정하기 때문에 중요한 옵션ㄷ이다.

strong
입력되는 변수의 소유권을 가져온다. referece count를 1증가시켜서
내가 소유하고 있는 동안에는 release되지 못하도록 한다.
이전의 retail 옵션과 동일한 기능, iOS 5.0이상 부터는 strong으로
변경되었다.
retain
strong과 같이 소유권을 가져온다는 의미해서 러퍼런스 카우트를 증가
시킨다.
week
week은 strong과 갈이 iOS5 이상 OS X v10.7에서 추가된 것으로
객체의 레퍼런스를 증가시키지 않는다. 따라서 언제가 객체가 릴리즈
되어서 메모리에서 사라질 수 있다. 이렇게 사라지면 자동으로 nil로
설정된다. ( 이전 버전에는 같은 목적으로 assign을 사용했다.)
copy
주어진 객체를 복사하는 것으로 strong의 경우 같은 객체를 레퍼런스
카운드를 사용해서 소유권을 가져오는 것이라면 copy는 아예 같은 값을
같는 객체를 새로 생성하는 것이다.
assign
기본 값으로 값을 복사하는 것이다. week과 유사하나 약간 다른데
week는 객체를 다룬다. 하지만 assign은 스칼라값 ( NSInteger와 같은 )
을 복사한다는 의미이다.

3.4 쓰레드 세이프 이거나 아니거나

nonatomic
setter가 쓰레드 세이프하면 쓰레딩 환경에서 아무 걱정 없이 안전에 하게
사용할 수 있겠지만 이 세상에 공짜가 없듯이 여기에서도 비용이 발생한다.
성능이다. 간단한 명령이지만 이것이 반복되어서 사용되다면 문제가 될것이다.
그리고 쓰레드 세이프여야 필요가 꼭 있는 코드를 작성할 일도 많지 않다.
여러가지 이유에서 이 옵션은 많이 사용이 된다.
atomic
setter가 쓰레드 세이프하도록 설정 ( 기본 값 )

이렇게 많은 옵션들이 사용되고 있다. 자주 사용될 옵션은 몇개 되지 않는다. 하지만 어떤 옵션들이 어디에 사용되는지 알아 두는 것도 Objective-C 상식에 도움이 될것이니 알아 두도록 하자.


4. Synthesize와 그 밖에..

앞에서 프로퍼티의 옵션들에 대해서 알아보았다. @property와 쌍을 이루는 것이 @synthesize 이다. @synthesize와 같이 정의 부분에 사용될 수 있는 것이 @dynamic 이라는 것도 있으니 알아보도록 하자.


@synthesize

@synthesize는 프로퍼티 코드 생성을 컴파일러에게 지시한다. 사용되는 위치는 @implementation과 @end 사이에 정의가 한다.

 @synthesize value;

위 예제에서 value는 @property에서 선언한 프로퍼티 이름으로 보통 내부적으로 사용되는 변수 이름도 동일한 value가 된다. 하지만 프로퍼티를 통해서 저장할 실제 멤버 변수 이름이 _value라면 어떨까? 이때는 다음과 같이 설정 해야 한다.

 @synthesize value = _value;

위에서 설명한 _value에 대해서 조금 더 알아보자.

프로퍼티는 메소드이고 이 메소드가 실제 멤버변수인 _value에 값을 읽거나 변경하는 식이다.

한가지 더…

최근에는 프로퍼티를 지정하면서 멤버 변수를 만들지 않아도 된다. 따라서 최근 버전의 Objective-C를 사용하고 있다면 그런 수고로움이 사라졌다.

다음 코드를 보자.

@interface Test : NSObject
{
    // member variable
    NSString *value;
}
@property (nonatomic, strong) NSString* value;
@end


@implementation Test
    @synthesize value;
@end

이 코드는 멤버변수를 선언하고 이 멤버변수를 외부에 노출시키기 위한 프로퍼티를 정의한 것이다. @synthesize에 프로퍼티 이름만 적으면 프로퍼티가 내부적으로 사용된 멤버 변수가 동일한 변수를 사용하고 있는 것이다.

@interface Test : NSObject
@property (nonatomic, strong) NSString* value;
@end


@implementation Test
    @synthesize value;
@end

즉, 위 코드와 같이 사용해도 된다. ( 어떤 버전부터 그런지는 조사 필요. 혹시 아시면 댓글 달아주세요.)


@dynamic

@dynamic은 컴파일러에게 setter/getter 코드를 생성하지도 말고 경고도 내지 말라고 지시하는 것이다. runtime으로 setter/getter를 설정하는 메커니즘이 있는 경우에만 사용하게 된다.

현재로는 Core Data에서 모델 클래스를 정의할 때만 사용되고 있다.



5. 사용방법

이제 마지막으로 프로퍼티로 설정한 변수를 어떻게 사용할지 알아보도록 하자.

 @interface Test : NSObject
 @property (nonatomic, copy) NSString *value;
 @end

위와 같이 Test 클래스의 헤더가 선언 되었을때 사용법은 다음과 같다.

// 생성
Test *t = [[Test alloc] init];

// set / get
t.value = @"sdfsdf";
NSString *ss = t.value;

// 메소드 이용
[t setValue:@"sdf"];
ss = [t value];

위 코드처럼 메소드를 사용하는 방식이 있는가 하면 .연산자를 사용하는 방식이 있다. 그 어떤 방식을 사용해도 동일하다.

반응형