이 항목은 작성 중인 항목입니다. 내용이 불완전할 수 있습니다. 충돌의 위험이 있으니 history를 확인한 후 작성하는 것을 권장합니다. 이 문서가 오랫동안 편집되지 않았다면 이 틀을 제거해 주세요.
@interface
명령과 @end
명령이다.@interface
명령은 선언하는 클래스의 이름과 상속하는 부모 클래스 이름, 그리고 쉼표로 구분된 준수할 프로토콜 이름의 목록을 인자로 받는다. 형식은 다음과 같다:@interface 클래스 이름 : 부모 클래스 이름 <준수할 프로토콜 이름 목록>여기서 부모 클래스 이름과 준수할 프로토콜 이름 목록은 선택적이다. 즉, 어떠한 클래스도 상속하지 않아 그 자신이 최상위 뿌리(root) 클래스가 될 수도 있고 어떠한 프로토콜도 준수하지 않을 수 있다.
@end
명령은 @interface
명령의 영역을 닫는다.// 뿌리 클래스 @interface ParentClass1 @end // 뿌리 클래스를 상속한 클래스 @interface ChildClass1: ParentClass1 @end // 또 다른 뿌리 클래스이면서 동시에 Protocol1을 준수 @interface ParentClass2 <Protocol1> @end // 또 다른 뿌리 클래스를 상속하면서 동시에 (상속받은) Protocol1과 Protocol2, Protocol3를 준수 @interface ChildClass2: ParentClass2 <Protocol2, Protocol3> @end
// 클래스 메서드 선언문 + (반환형) 메서드_이름 ; // 인스턴스 메서드 선언문 - (반환형) 메서드_이름 ;선언문에서
(반환형)
부분은 생략 가능하다. 생략하면 기본 반환형인 id
가 사용된다.// 알림: 읽기 쉽도록 메서드 이름에 언더스코어(_)를 포함시켰다. // 즉, 여기서 메서드 이름의 언더스코어는 필수요소가 아닌, 메서드 이름의 일부이다. // 인자를 받지 않는 경우. 메서드_이름 // 인자를 하나 받는 경우. // 이 경우, 메서드 이름은 '메서드_이름과_인자:'가 된다. 메서드_이름과_인자:(인자형)인자_이름 // 인자를 두 개 받는 경우. // 이 경우, 메서드 이름은 '메서드_이름과_인자1:인자2:'가 된다. 메서드_이름과_인자1:(인자형)인자_이름1 인자2:(인자형)인자_이름2 // 인자를 세 개 받는 경우. // 이 경우, 메서드 이름은 '메서드_이름과_인자1:인자2:인자3:'이 된다. 메서드_이름과_인자1:(인자형)인자_이름1 인자2:(인자형)인자_이름2 인자3:(인자형)인자_이름3 // 이 정도면 이해했겠지.
// 클래스 메서드. 반환형은 char *. // 메서드 이름은 'iTakeNoParameter'. +(char *)iTakeNoParameter; // 클래스 메서드. 반환형은 id. // 메서드 이름은 'allocWithZone:'. // 하나의 인자를 받으며, 그 형은 NSZone *, 이름은 aZone. +allocWithZone:(NSZone *)aZone; +(id)allocWithZone:(NSZone *)aZone; // 같은 표현 // 인스턴스 메서드. 반환형은 NSInteger. // 메서드 이름은 'numberOfCharacter:inString:'. // 인자는 두 개를 받는다. 첫번째는 unichar형의 ch, 두번째는 NSString *형의 str. -(NSInteger)numberOfCharacter:(unichar)ch inString:(NSString *)str; // 인스턴스 메서드. 반환형은 없음. // 메서드 이름은 'drawRectangleOfWidth:height:upperLeftX:upperLeftY:'. // 인자는 네 개를 받는다. 모든 인자는 CGFloat형이고 그 이름은 각각 w, h, x, y이다. -(void)drawRectangleOfWidth:(CGFloat)w height:(CGFloat)h upperLeftX:(CGFloat)x upperLeftY:(CGFloat)y;콜론 역시 메서드 이름의 일부임을 유의하자.
@interface
명령 바로 다음이다:@interface ExampleClass: ExampleSuperClass <ExampleProtocol> { // 인스턴스 변수 선언 시작 int exampleInteger; // struct를 만들 때처럼 변수를 선언해주면 된다. char exampleCharacter; NSString *exampleString; // 기타 인스턴스 변수를 선언한다. } // 인스턴스 변수 선언 끝 // 메서드 선언부 @end
@private
, @protected
, @public
명령을 사용해서 인스턴스 변수의 가시성을 조절할 수 있다:@interface ExampleClass: ExampleSuperClass <ExampleProtocol> { double iAmProtectedByDefault; // 기본 가시성은 @protected이다. @private int privateInteger; // @private 변수는 이 클래스 소속의 메서드에서만 접근 가능하다. char privateCharacter; @protected // 이렇게 다른 가시성 명령을 만나기 전까지는 이전 가시성 명령이 계속 적용된다. int protectedInteger; // @protected 변수는 이 클래스와 상속받은 하위 클래스 소속의 메서드에서만 접근 가능하다. char protectedCharacter; @public int publicInteger; // @public 변수는 개나 소나 다 접근 가능하다. char publicCharacter; @private int privateInteger2; @public char publicCharacter2; @protected float protectedFloat; int iAmProtectedToo; } // 메서드 선언부 @end가시성 명령은 어디까지나 선택적이다. 명시적인 가시성 명령이 없으면 인스턴스 변수에는 기본적으로
@protected
가 적용된다.@interface
내부에 인스턴스 변수 선언을 넣는 것에 반대하는 입장이다. 따라서 이렇게 할 수도 있다는 것만 알고 다음으로 넘어가도록 하자. 다른 방법은 클래스 정의 부분에서 알아볼 것이다.@public
으로 놓는 미친 짓을 하는 게 아니라면 접근자와 변경자를 써야 한다.-(int)getInt { return anInteger;
}나 -(void)setInt:(int)newInt { anInteger = newInt;
} 같은 중복 코드를 작성하는 건 귀찮고 따분한 일이 아닐 수 없다.@property
명령을 사용하면@property( 쉼표로 구분된 프로퍼티 설정 목록 ) 변수형 변수이름 ;그리고 프로퍼티 선언문은 메서드 선언부에 같이 들어간다.
getter = accessorName
name
과 age
인 경우, 기본 접근자 이름 역시 name
과 age
이다.setter = mutatorName
set변수명
의 형태가 된다.name
과 age
일 경우, 기본 변형자 이름은 setName
, setAge
가 된다. 자동 캐멀 케이싱에 주의할 것.readonly
: 접근자만 자동으로 생성되고 변형자는 생성되지 않는다. 이 경우, 아래 메모리 관리 정책은 쓸모가 없다.readwrite
: 접근자와 변형자가 자동으로 생성된다.readwrite
이다.assign
: 변형자는 인자로 받은 값을 인스턴스 변수에 단순 대입한다. 인자로 원시형을 받는 경우에는 반드시 이 선택자를 써야 한다. 인자로 객체를 받는 경우, 이 객체의 생명주기에 전혀 간섭할 수 없다.retain
, strong
: 변형자의 인자로 받은 객체에 대해 강한 소유권을 행사한다. 원시형에는 이 선택자를 적용할 수 없다. 이 변수가 어떠한 객체를 들고있는 동안 해당 객체는 절대 소멸되지 않는다.copy
: 변형자의 인자로 받은 객체를 복제하여 사본 객체를 인스턴스 변수에 넣는다. 원시형에는 이 선택자를 적용할 수 없다. 사본 객체에 대해서는 강한 소유권을 행사한다. 원본 객체의 생명주기에는 간섭하지 않는다.weak
: 변형자의 인자로 받은 객체에 대해 약한 소유권을 행사한다. 원시형에는 이 선택자를 적용할 수 없다. 객체는 강한 소유권을 행사하는 주체가 없어지는 즉시 소멸되는데, 이 때 약한 소유권을 행사하는 주체는 모두 nil
로 재설정된다.assign
이다.nonatomic
: 접근자/변형자가 인스턴스 변수에 접근할 때 잠금(lock)을 사용하지 않도록 한다. 다중 스레드 환경에서 사용할 때는 주의하여야 한다. 다만, 읽기 전용(readonly) 프로퍼티에 적용하면 소소하게나마 성능 향상을 꾀할 수 있다.atomic
선택자는 없다. 그냥 nonatomic
을 쓰지 않으면 원자적으로(== lock 사용) 동작한다.@interface Developer: Employee <CoffeeConsuming> { @private NSInteger employeeSince; BOOL fired; } // NSString *형의 name. // 생성되는 접근자: -(NSString *)name;. // 생성되는 변형자: 생성 안 됨. // 인스턴스 변수 자동 생성 여부: 생성됨. // 원자성: 원자적이지 않음. @property(readonly, nonatomic) NSString *name; // NSInteger형의 employeeSince. // 생성되는 접근자: -(NSInteger)employeeSince;. // 생성되는 변형자: 생성 안 됨. // 인스턴스 변수 자동 생성 여부: 생성 안 됨. // 원자성: 원자적임. @property(readonly) NSInteger employeeSince; // BOOL형의 fired. // 생성되는 접근자: -(BOOL)isFired;. // 생성되는 변형자: -(void)setFired:(BOOL)fireHim;. // 인스턴스 변수 자동 생성 여부: 생성 안 됨. // 메모리 관리 정책: 단순 대입. // 원자성: 원자적이지 않음. @property(readwrite, nonatomic, getter = isFired) BOOL fired; // NSArray *형의 thingsToDo. // 생성되는 접근자: -(NSArray *)thingsToDo;. // 생성되는 변형자: -(void)assignThingsToDo:(NSArray *)jobs;. // 인스턴스 변수 자동 생성 여부: 생성됨. // 메모리 관리 정책: 복제. // 원자성: 원자적임. @property(readwrite, copy, setter = assignThingsToDo) NSArray *thingsToDo; @end
@implementation 클래스 이름역시나
@end
명령으로 @implementation
의 영역을 닫는다.@implementation EmptyClass // The Beauty of Emptiness. @end
@implementation ExampleClass +(char *)iTakeNoParameter { return "asdf"; } +(id)allocWithZone:(NSZone *)aZone { // Vendor specific implementation. } -(NSInteger)numberOfCharacter:(unichar)ch inString:(NSString *)str { NSUInteger j = [str length], k = 0; unichar *asdf = (unichar *)malloc(sizeof(unichar) * j); if(NULL == asdf) return -1; [str getCharacters:asdf range:NSRangeMake(0, j)]; for(NSUInteger i = 0; i < j; ++i) { if(ch == asdf[i]) ++k; } return k; } -(void)drawRectangleOfWidth:(CGFloat)w height:(CGFloat)h upperLeftX:(CGFloat)x upperLeftY:(CGFloat)y { // implementation may vary. } @end
@implementation ExampleClass { // 인스턴스 변수 선언 시작 int protected; // 역시 기본 가시성은 protected이다. 하지만....... @public char inaccessibleFromOutside; // @public 변수가 구현부에 숨어있어서, 외부에서 절대 접근이 불가능하다. @protected long protectedButInaccessibleFromChilds; // @protected 변수가 구현부에 숨어있어서, 상속하는 클래스는 접근할 수 없다. @private unsigned long long private; } // 인스턴스 변수 선언 끝 @end