accessor method

클래스의 선언과 정의를 올바르게 실시해, Object 루트 클래스의 alloc 메소드를 호출하면, 클래스의 인스턴스가 생성됩니다. 클래스의 정의는 구조체의 선언과 같은 것으로, 인스턴스의 생성하는 것은, 구조 체형의 변수를 정의해 구조체를 표현하기 위해서 필요한 메모리를 할당하는 행위를 닮아 있습니다. 다만, 클래스는 멤버 변수와 그것을 처리하는 전용의 함수를 관련짓고 있는 점으로, 구조체와 크게 다릅니다.

전회에서, 클래스를 선언해, 메소드를 정의하고 그것을 호출하는 것을 실시했습니다. 전회의 메소드는 인수나 반환값을 교환하지 않는 지극히 단순한 것이었지만, 본 회에서는 보다 복잡한 메소드를 실현하기 위해서, 메소드의 상세를 설명합니다. 특히, 메소드는 함수와는 달리, 설계론의 개념으로부터 인스턴스나 클래스의 역할에 관련지을 수 없으면 안됩니다.

예를 들면, 2차원 좌표의 점을 나타내는 Point 클래스를 작성한다고 합니다. 이 경우, Point 클래스는 좌표 X 와 Y 를 제공하지 않으면 안됩니다. Point 클래스는 int 형태의 인스턴스 변수 x 과 y 를 선언해, 이러한 값을 제어하는 메소드군을 제공할 필요가 있습니다.

원칙으로서 클래스의 인스턴스 변수는 클래스의 외부에서는 액세스 할 수 없습니다. 왜냐하면, 변수가 자유롭게 변경되어 버리면, 변수의 의미나 입출력하는 데이터의 사양이 변경되었을 경우 등, 보수성이나 코드 전체의 유연성이 저하해 버리기 때문입니다. 거기서, Objective-C는 변수에 액세스 하기 위해서 메소드를 사용합니다. Point 클래스의 기본적인 선언은, 다음과 같이 되겠지요.

@interface Point : Object
{
	int x, y;
}
- (void)setPoint:(int)ptx:(int)pty;
- (int)getX;
- (int)getY;
@end

이 클래스의 선언으로는, 좌표를 보존하기 위한 변수 x 와 y 를 선언해, 한층 더 이 변수에 클래스의 외부로부터 액세스하기 위한 메소드를 선언하고 있습니다. setPoint 메소드는 x 와 y 의 값을 변경하기 위한 메소드로, getX 와 getY 메소드는, 각각의 변수를 취득하기 위한 메소드입니다. 이러한 메소드를, 특히 accessor method라고 부릅니다. 또, 이와 같이 인스턴스의 성질에 관한 외부에 제공하기 위한 정보를, 특히 프롭퍼티라고 부르기도 합니다.

인수를 받는 메소드는, 인수형을 ( ) 의 안으로 지정해, 바로뒤에 가인수의 이름을 지정합니다. 이 때의 ( ) 는 캐스트식과 같고, 형태를 지정하지 않는 경우는 반환값과 같게 id 형태가 디폴트로 되어 있습니다. 복수의 인수가 있는 경우, 한번더 콜론 : 을 이어 지정합니다. 보다 구체적으로는, 메소드의 선언은 다음과 같이 됩니다.

- (반환값형)메소드명 : (인수형) 변수명 라벨 : ....

복수의 인수를 받는 메소드는, 변수명의 뒤에 라벨을 지정하는 일이 있습니다. 비교적,Objective-C 의 세계에서는 라벨을 지정하는 것이 습관이 되고 있습니다만, 이 라벨과 콜론은 메소드명의 일부로서 인식됩니다.

라벨과 코론이 이름의 일부로서 인식되고 있기 때문에,Objective-C는 같은 반환값, 인수, 메소드명을 가지는 메소드를 구별해 호출할 수 있습니다. 예를 들면 (void)setPoint:(int):(int) 그렇다고 하는 메소드와 (void)setPoint:(int) label:(int) 메소드는 다른 메소드로서 선언, 정의할 수 있습니다.

인수 첨부의 메소드를 호출하려면 , 메세지에서 메소드명에 있는 인수에 건네주는 값을 지정합니다. 라벨을 지정하고 있지 않는 전자의 setPoint 의 경우 [obj setPoint:x:y] 의 형태로 호출하는데 대해, 라벨 첨부의 메소드는 [obj setPoint:x label:y] 의 형태로 호출합니다. 인수의 수나 라벨만 다르면, 메소드명이 같아도 구별해 호출할 수 있는 구조입니다. 특정의 기능을 제공하는 메소드를, 여러가지 형태에 대응해 제공하고 싶은 경우에 라벨이 필요합니다. setPoint 메소드를,int 형태 이외의 정수에도 대응시키고 싶은 경우 등은 중요하겠지요.

#import <stdio.h>
#import <objc/Object.h>

@interface Point : Object
{
	int x, y;
}
- (void)setPoint:(int)ptx int:(int)pty;
- (int)getX;
- (int)getY;
@end

@implementation Point
- (void)setPoint:(int)ptx int:(int)pty {
	x = ptx;
	y = pty;
}
- (int)getX {
	return x;
}
- (int)getY {
	return y;
}
@end

int main() {
	id point1 , point2;
	point1 = [Point alloc];
	point2 = [Point alloc];

	[point1 setPoint:16 int:32];
	[point2 setPoint:256 int:128];

	printf("point1:X=%d, Y=%d\n", [point1 getX] , [point1 getY]);
	printf("point2:X=%d, Y=%d\n", [point2 getX] , [point2 getY]);
	return 0;
}

이 프로그램의 Point 클래스는,2 개의 정수형 인수를 받는 setPoint:int: 메소드와 설정되어 있는 좌표를 돌려주는 getX,getY 메소드를 선언하고 있습니다. setPoint 메소드의 선언은 setPoint(int)ptx:(int)pty 에서는 문제 없습니다만, 장래 setPoint 를 확장하는 것을 생각했을 경우는, 라벨을 지정해야 겠지요.

main() 메소드에서는 alloc 메소드를 이용해 Point 클래스의 인스턴스를 2 개 생성하고 있습니다. point1 변수와 point2 변수가 가리키는 인스턴스는 차이가 나기 위해, 각각의 인스턴스로 설정한 값은, 개별의 메모리에 보존되고 있는 것이, 출력 결과로부터 확인할 수 있습니다.


암묵의 self

메소드의 스코프 범위내에는, 가인수나 선언된 변수 이외에, 암묵의 변수로서 self가 정의되고 있습니다. self 변수는 id 형태로, 항상 메소드를 호출한 인스턴스를 참조하고 있습니다. 즉, 메소드를 실행하고 있는 오브젝트 자신을 나타내는 변수가 self 입니다. (C++의 this 라고 생각하면 됩니다)

self 는 메소드 이외의 장소에서는 사용하지 못하고, 항상 메소드 실행 코드 부분에서만 암묵적으로 존재하고 있습니다. self 를 이용하는 것에 의해서, 오브젝트의 인스턴스 변수에 오브젝트로부터 참조할 수 있습니다. 이것은, 메소드의 가인수의 변수명이 인스턴스 변수명을 가려버렸을 때 등에 이용할 수 있습니다. accessor method에서는, 특히 중요한 존재가 되겠지요.

#import <stdio.h>
#import <objc/Object.h>

@interface Point : Object
{
	int x, y;
}
- (void)setPoint:(int)ptx int:(int)pty;
- (int)getX;
- (int)getY;
@end

@implementation Point
- (void)setPoint:(int)x int:(int)y {
	self->x = x;
	self->y = y;
}
- (int)getX {
	return x;
}
- (int)getY {
	return y;
}
@end

int main() {
	id point1 , point2;
	point1 = [Point alloc];
	point2 = [Point alloc];

	[point1 setPoint:32 int:64];
	[point2 setPoint:256 int:128];

	printf("point1:X=%d, Y=%d\n", [point1 getX] , [point1 getY]);
	printf("point2:X=%d, Y=%d\n", [point2 getX] , [point2 getY]);
	return 0;
}

이 프로그램의 setPoint:int: 메소드는, 가인수로 선언한 이름이 인스턴스 변수 x 와 y 에 충돌하고 있습니다. 이것 자체는 문제가 아닙니다만, 이 메소드의 스코프 범위에서는 인스턴스 변수가 가려져 버립니다. 거기서, 인스턴스 변수에 액세스 하는 수단으로서 현재의 메소드를 실행하고 있는 오브젝트를 참조하는 self 변수를 사용하고 있습니다. 클래스의 외부로부터 인스턴스 변수에 참조할 수 없습니다만, 메소드는 클래스의 내부이므로, 오브젝트로부터 직접 인스턴스 변수에 액세스 할 수 있습니다. 프로그램에서는 self->x 그렇다고 하는 형태로, 인스턴스 변수에게 건네진 데이터를 보존하고 있습니다.

또, 메소드로부터 같은 인스턴스가 다른 메소드를 호출하는 경우에도 self가 필요하게 됩니다. 몇개의 메소드가, 기능의 일부를 공유하고 있는 설계등에서는, 동일 클래스의 다른 메소드를 호출하는 것이 필요하게 되는 일이 있습니다.

#import <stdio.h>
#import <objc/Object.h>

@interface Test : Object
- (void)methodA;
- (void)methodB;
@end

@implementation Test
- (void)methodA {
	printf("method A\n");
	[self methodB];
}
- (void)methodB {
	printf("method B\n");
}
@end

int main() {
	[[Test alloc] methodA];
	return 0;
}

이 프로그램의 Test 클래스에서는,methodA 메소드로부터 methodB 메소드를 self 오브젝트를 사용해 호출하고 있습니다. self 하 methodA (을)를 실행하고 있는 인스턴스이므로, 동일 인스턴스의 methodB (을)를 호출하고 있는 것이 됩니다.

원문 http://wisdom.sakura.ne.jp/programming/objc/objc4.html
번역의 귀차니즘으로 번역기 돌린 후 원문과 대조하면서 어색한 부분만 재수정한겁니다.
어휘 사용에서 다소 어색한 부분이 있더라도 양해바랍니다.

Posted by tklee

댓글을 달아 주세요