Objective-C는 컴파일러가 생성하는 객체의 구조에 접근 가능하게 하는 API를 제공한다. 이를
이용하면 GNU Objective-C의 Object나 MacOS X의 NSObject같은 최상위 클래스를 쉽게 작성할 수 있다.
반드시 그렇게 해야 할 이유는 없지만 말이다.
API를 사용하려면 다음의 파일들을 #include 해야 한다.
- objc/objc.h
- objc/objc-api.h
- objc/objc-class.h (MacOS X)
Objective-C가 생성하는 모든 객체들은 헤더 부분에 클래스 객체에 대한 포인터를 가지며, 데이터 부분은 클래스 정의에 따라 달라지게 된다. 객체의 헤더 부분은 struct objc_object에 정의 되어 있다.
/* GNU Objective-C - objc/objc.h */
struct objc_object
{
struct objc_class* class_pointer;
};
MacOS X에서는 class_pointer대신 isa라는 이름이 사용된다.
struct objc_class는 클래스 객체의 구조를 정의한다. 클래스 역시 객체이므로
메타클래스라고 하는 자신의 클래스 객체에 대한 포인터를 가지며, 크기, 이름, 부모 클래스등 생성할 객체에 대한 여러 가지
정보를 담고 있다.
/* GNU Objective-C - objc/objc.h */
struct objc_class
{
struct objc_class* class_pointer;
struct objc_class* super_class;
const char* name;
long version;
unsigned long info;
long instance_size;
struct objc_ivar_list* ivars;
struct objc_method_list* methods;
struct sarray* dtable;
struct objc_class* subclass_list;
struct objc_class* sibling_class;
struct objc_protocol_list *protocols; void* gc_object_type;
};
최상위 클래스는 클래스 포인터를 초기화 하고 객체의 생성과 소멸을 담당하는 메서드들을 제공해야 한다. GNU Objective-C의 Object처럼 alloc, init, free, new를 작성해보자.
/* Object.h */
#include <objc/objc.h>
#include <objc/objc-api.h>
#ifdef __APPLE__
#include <objc/objc-class.h>
#endif
@interface Object
{
struct objc_class* isa;
}
+ (id) alloc;
+ (id) new;
- (id) init;
- (void) free;
@end
- isa - 클래스 포인터
- alloc - 객체가 필요한 메모리 공간을 할당한다.
- free - 객체가 사용한 메모리 공간을 해제한다.
- init - 객체를 초기화 한다.
- new - [[object alloc] init]
alloc를 제외한 나머지는 다음과 같이 간단하게 구현될 수 있다.
/* Object.m */
#include "Object.m"
#include <stdlib.h>
@implementation Object
+ (id) alloc
{
return alloc_object (self);
}
+ (id) new
{
id obj = [self alloc];
if (obj == nil) return nil;
return [obj init];
}
- (id) init
{
return self;
}
- (void) free
{
if (self != nil) free (self);
}
@end
객체가 필요한 공간을 할당하려면 객체의 크기를 알아야 한다. 객체의 크기는 클래스의 instance_size 필드에 저장이 되는데, 실제 필요한 크기는 여기에 헤더부분(클래스포인터)의 크기를 포함해야 한다.
/* Object.m */
static struct objc_object* alloc_object (struct objc_class* class)
{
size_t size;
struct objc_object* obj;
/* 객체가 필요로 하는 공간의 크기 */
size = sizeof(struct objc_object) + class->instance_size;
/* 공간을 할당한다 */
obj = (struct objc_object*)calloc(1, size);
if (obj == NULL) return nil;
/* MacOS X는 class_pointer대신 isa라는 이름을 사용하므로 #ifdef로 처리한다 */
#ifdef __APPLE__
obj->isa = class;
#else
obj->class_pointer = class;
#endif
return obj;
}
MacOS X는 추가적으로 forward:sel:메서드를 필요로 한다.
#ifdef __APPLE__
- (id) forward: (SEL)sel: (marg_list)args
{
return self;
}
#endif
이제 위에서 만든 최상위 클래스를 사용해 보도록 하자.
#include "Object.h"
#include <stdio.h>
@interface Child: Object
{
int a;
}
- (id) init;
- (void) print;
@end
@implementation Child
- (id) init
{
[super init];
a = 2871;
return self;
}
- (void) print
{
printf ("a = %d\n", a);
}
@end
int main ()
{
id t;
t = [[Child alloc] init]; /* 개체를 생성하고 초기화 한다 */
[t print]; /* print메시지를 보낸다 */
[t free]; /* 개체를 없앤다 */
return 0;
}