클래스형

Objective-C가 생성한 클래스의 인스턴스는 id 형태의 변수에 보존할 수 있었습니다. id 형태는 void *를 닮은 존재로, 클래스에 관계없이, 인스턴스를 보존하는 범용적인 오브젝트형입니다. 그 때문에, id 형태의 변수의 실체는, 컴파일시는 아니고 실행시에 판정된다고 하는 특징이 있습니다.

그러나, 실행시에 인스턴스를 조사하는 것은, 예를 들면 메소드를 호출하기 위해서, 오브젝트에 대해서 메세지를 송신해도, 그 메세지에 대응한 메소드가 존재하지 않는다고 할 가능성이 있습니다. 다음과 같은 프로그램은 그 전형적인 예이지요.

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

@interface A : Object
- (void) Write;
@end

@implementation A
- (void) Write {
	printf("I am the born of my sword.\n");
}
@end

int main() {
	id obj1 = [A new];
	id obj2 = [Object new];

	[obj1 Write];
	[obj2 Write];	//실행시 에러

	return 0;
}

이 프로그램은 선언한 A 클래스의 인스턴스를 생성해 Write 메세지를 송신하고 있습니다. 이와 같이 Object 클래스의 인스턴스를 생성해, Write 메세지를 송신하고 있습니다. Object 클래스에는 Write 메소드 등은 존재하지 않습니다만, 컴파일 할 수 있습니다. 이것은, Objective-C 의 메세지의 호출이 실행시에 판정되는 것을 나타내고 있습니다.

컴파일 된 프로그램을 실행하면, obj1 변수에 대해서 Write 메세지의 송신은 성공합니다만, obj2 변수는 Object 클래스의 오브젝트이므로 Write 메소드는 존재하지 않습니다. 그 때문에, obj2 변수에 대해서 Write 메세지를 송신해도, 실행시 에러가 발생합니다.

이와 같이, id 형태는 범용적이고, 오브젝트라고 하는 단위로 클래스형의 인스턴스 데이터를 참조하려면 편리합니다만, 특정의 클래스를 상정해 취급하는 경우에는 적합하지 않습니다. 특히, 대규모 프로그램이 되고, 많은 인스턴스를 생성하는 경우는, id 형태를 대량으로 선언하면, 어느 변수에 어느 클래스형의 오브젝트를 보존하고 있는지 어떤지가 모르게 되어 버립니다.

형태의 판정을 실행시가 아니고, 컴파일시에 실시할 수 있으면, 컴파일러가 형태를 체크하는 정보로서 이용할 가능성이 있습니다. 구현에 의존하는 문제입니다만, 형태가 컴파일시에 판명되면, 메세지를 조사하고, 대상의 클래스형으로 지정한 메소드가 존재할지를 조사해 필요에 따라서 경고를 뿌립니다. 컴파일시에, 정적으로 클래스형을 판정시키려면 ,id 형태가 아닌 클래스명을 형태로 한 클래스형의 포인터를 사용해 인스턴스를 참조합니다. 예를 들면, Point 클래스 전용의 변수는 Point 형태에의 포인터로서 선언합니다.

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

@interface A : Object
- (void)Write;

@end


@implementation A
- (void)Write {
	printf("A . Write Method\n");
}
@end

int main() {
	A * obja = [A new];
	[obja Write];
	[obja free];
	return 0;
}

이 프로그램은, 선언된 A 클래스의 인스턴스를 작성해, A * 형태의 변수 obja 에 보존하고 있습니다. 지금까지는 id 형태에 오브젝트를 보존해 왔습니다만, 이 프로그램에서는 명시적으로 A 클래스형인 것을 지정해 있습니다.

클래스형의 포인터는, 메모리상의 실체인 인스턴스의 주소를 보존하기 위해서 이용됩니다. id 형태도 인스턴스의 주소를 보존하기 위한의 것입니다만, 형태로서는 클래스형을 지정하는 편이 명확합니다. 통상, Objective-C 의 클래스형 변수는 구조 체형의 변수와 같이, 정적인 변수로서 선언할 수 없습니다. 클래스형의 변수는 반드시 포인터형으로서 다루어져야 하는 것입니다.

SmallTalk를 기반으로하는 Objective-C 언어에서는, 비교적 id 형태가 이용되고 있는 경향이 있습니다만, 현대의 객체 지향 언어의 표준적인 기술 방법으로부터 생각하면, 클래스형을 명기한 변수를 사용하는 것이 좋을 것입니다. id 형태를 사용하는 것은, alloc 이나 init 과 같은, 돌려주는 값이 다양화되고 있는 메소드 등에 한정해야 합니다.

그럼, 클래스형의 변수는, 그 클래스형의 인스턴스에의 포인터 밖에 대입할 수 없는 것일까요. C 언어의 원리를 생각하면, 결국은 포인터에 지나지 않기 때문에 그러한 일은 없습니다.


Posted by tklee