= Objective-C = [[include(틀:작성중)]] ''오브젝티브 씨''라고 읽으면 된다. 애칭은 ''옵씨''. 브래드 콕스(Brad Cox)와 톰 러브(Tom Love)가 만든, '''20세기의 걸작 언어 Jr.'''라고 생각하면 당신은 멋쟁이. 안시(ANSI) [[C]]에 객체지향 층을 얹어 만들었다. 따라서 실행 시 반드시 런타임(libobjc.dylib 또는 objc.dll)의 도움을 받아야 작동 가능하다. 물론 런타임은 [http://opensource.apple.com/source/objc4/ 애플 오픈 소스 사이트]에 공개돼있으므로 '''능력이 있다면''' 얼마든지 정적 라이브러리로 만들어 붙여버려도 된다. == 역사 == 초창기(넥스트가 먹기 전?) 옵씨는 C에서 [[객체지향]]을 지원하도록 문법을 확장한, 일종의 문법적 편의사항(syntactic sugar)으로 구현됐다. 옵씨 컴파일러는 옵씨 코드를 읽어 C 코드를 뱉었고, C 컴파일러가 그걸 바이너리로 어셈블했다. 하지만 넥스트(NeXT Inc.)가 옵씨를 집어삼킨 이후, 옵씨 코드에서 바로 바이너리로 직행하는 컴파일러를 만들었다. 이를 위해, 예전에는 GCC를 이용했지만 요즘은 애플 주도의 [[LLVM/Clang]]을 이용해 컴파일한다. 물론 오픈 소스 LLVM/Clang 3와 Apple LLVM/Clang 4 모두 옵씨 코드를 컴파일할 수 있지만, 클로즈드 브랜치인 Apple LLVM/Clang 4가 훨씬 월등한 어셈블 능력을 보여준다고. 여담으로 [[FreeBSD]] 재단에서 오픈 소스 LLVM/Clang 3를 다루던 사람이 [http://bsdtalk.blogspot.kr/2013/11/bsdtalk233-from-gcc-to-llvmclang-with.html 말하길], 참고삼아 [[GCC]] 4.2의 옵씨 담당 부분 소스 코드를 봤다가 기절할 뻔했다고. 무려 일만 줄이 넘어가는 '''단일''' C 파일 하나로 옵씨의 모든 걸 처리했단다. 이 GCC 확장에 대해서는 여러 루머가 있는데, 가장 유명한 걸 고려한다면 잡스가 빡쳐서 엿먹어라 하는 수준으로 GCC에 똥을 던진 듯. == 문법 == [[C]] 소스 코드는 곧 옵씨 소스 코드이다. 앞서 말했듯, 옵씨는 C에 확장을 얇게 바른 언어에 가깝다. 하지만 그렇다고 옵씨 소스 코드가 곧 C 소스 코드인 건 아니다. 이 단에서는 옵씨만의 고유한 문법과 기호에 대해 설명한다. === 클래스 선언 및 정의 === ==== 클래스 선언 ==== 클래스 선언은 외부로 노출 가능한 클래스 명세를 의미한다. 즉, 외부의 프로그래머들에게 '이 클래스는 이러이러한 기능을 가지고 있다'고 알리는 목적이다. 클래스 선언에 쓰이는 옵씨 고유의 문법은 `@interface` 디렉티브와 `@end` 디렉티브이다. `@interface` 디렉티브는 선언하는 클래스의 이름과 상속하는 부모 클래스 이름, 그리고 반점으로 구분된 준수할 프로토콜 이름의 목록을 인자로 받는다. 형식은 다음과 같다: {{{ @interface 클래스 이름 : 부모 클래스 이름 <준수할 프로토콜 이름 목록> }}} 여기서 부모 클래스 이름과 준수할 프로토콜 이름 목록은 선택적이다. 즉, 어떠한 클래스도 상속하지 않아 그 자신이 최상위 뿌리(root) 클래스가 될 수도 있고 어떠한 프로토콜도 준수하지 않을 수 있다. 아래에 몇 가지 예제가 있다: {{{ // 뿌리 클래스 @interface ParentClass1 @end // 뿌리 클래스를 상속한 클래스 @interface ChildClass1: ParentClass1 @end // 또 다른 뿌리 클래스이면서 동시에 Protocol1을 준수 @interface ParentClass2 @end // 또 다른 뿌리 클래스를 상속하면서 동시에 Protocol2와 Protocol3를 준수 @interface ChildClass2: ParentClass2 @end }}} 클래스 선언은 주로 헤더 파일에 넣는다. ==== 메서드 선언 ==== 메서드 선언 역시 선언의 일부이므로 클래스 선언 안에 들어간다. 메서드 선언문의 형식은 다음과 같다: //여기까지 작성함. 다음 코드는 옵씨에서 클래스를 선언하는 방법을 보여준다: {{{ // ZPExampleClass.h #import "ZPExampleSuperClass.h" #import "ZPExampleProtocol.h" @interface ZPExampleClass: ZPExampleSuperClass // 클래스 이름은 ZPExampleClass이고 ZPExampleSuperClass를 상속하며 ZPExampleProtocol 인터페이스를 구현할 의무가 있다. ZPExampleSuperClass는 NSObject를 상속한다고 가정하자. { // 이 안으로는 인스턴스 변수를 선언한다. @private char privateInstanceVariable; // 외부 프로그래머는 이 인스턴스 변수가 존재함을 알 수 있지만, 이 클래스 바깥에서 사용이 불가하다. @protected int protectedInstanceVariable; // 외부 프로그래머는 이 인스턴스 변수가 존재함을 알 수 있지만, 이 클래스를 상속해야만 사용 가능하다. @public int *publicInstanceVariable; // 개나 소나 다 쓸 수 있다. } // 인스턴스 변수 선언 끝. +(void)inJavaThisIsCalledAStaticMethod; // '클래스 메서드'라고 하며, 인스턴스가 아닌 클래스 단에서 호출 가능. 자바에는 '스태틱 메서드'라고 하는 비슷한 놈이 있다. -(int)thisIsANormalMethodTakingOneArgument:(int)anArgument; // 이 메서드의 이름은 thisIsANormalMethodTakingOneArgument:이다. 콜론까지 이름에 포함한다. -(char *)methodTakingTwoArgumentsStartingArgOne:(int)argOne andArgTwo:(char)argTwo; // 이름은 methodTakingTwoArgumentsStartingArgOne:andArgTwo:. -(void)willDoNothingWithArgOne:(char *)firstArg argTwo:(id)secondArg argThree:(void *)thirdImpact; -(void)takingVariableArgs:(int)requiredFirstArg, ...; @end }}} 그리고 다음은 선언한 클래스를 정의하는 코드이다: {{{ // ZPExampleClass.m #import "ZPExampleClass.h" @implementation ZPExampleClass { // 인스턴스 변수는 여기서도 선언할 수 있다. @private int privateInstanceVariable2; // 외부 프로그래머는 이 인스턴스 변수가 존재함을 알 수 없고, 쓸 수도 없다. @protected int protectedInstanceVariable2; // 외부 프로그래머는 이 인스턴스 변수가 존재함을 알 수 없고, 쓸 수도 없다. 상속받는 클래스야 쓸 수는 있겠지만, 존재 여부를 알지 못하니 무용지물. @public char *publicInstanceVariable2; // ....... 님 맞을래여? } // 인스턴스 변수 선언 끝. +(void)inJavaThisIsCalledAStaticMethod { NSLog(@"클래스 메서드 호출됨."); } -(int)thisIsANormalMethodTakingOneArgument:(int)anArgument { return anArgument + 1; } -(char *)methodTakingTwoArgumentsStartingArgOne:(int)argOne andArgTwo:(char)argTwo { char *tmp = (char *)malloc(sizeof(char) * (argOne + 1)); if(NULL != tmp) { for(int i = 0; i < argOne; ++i) tmp[i] = argTwo; } return tmp; } -(void)willDoNothingWithArgOne:(char *)firstArg argTwo:(id)secondArg argThree:(void *)thirdImpact { // 아무 것도 안 함. } -(void)takingVariableArgs:(int)requiredFirstArg, ... { // 추후 구현 예정. } @end }}} 마지막으로 클래스 인스턴스화 및 사용법이다: {{{ ZPExampleClass *asdf = [[ZPExampleClass alloc] init]; // +alloc과 -init은 NSObject에 선언돼있음. [asdf willDoNothingWithArgOne:"Ooh, sometimes, I get a good feeling, yeah." argTwo:nil argThree:NULL]; }}} == 구조 == === 객체의 구조 === 옵씨에서 임의의 객체를 가리키는 객체 포인터 `id`는 `objc.h`에 다음과 같이 선언돼있다: {{{ #if !OBJC_TYPES_DEFINED /// An opaque type that represents an Objective-C class. typedef struct objc_class *Class; /// Represents an instance of a class. struct objc_object { Class isa; }; /// A pointer to an instance of a class. typedef struct objc_object *id; #endif }}} 즉, `id`는 `struct objc_object`에 대한 포인터이고, `struct objc_object`는 무조건 그 첫 내부 변수로 `struct objc_class`에 대한 포인터를 들고 있다. 사용자가 어떠한 사용자 정의 클래스를 만들더라도 무조건 첫 내부 변수는 `isa` 포인터이다. 이 `isa`('이즈 어'라고 읽는다.) 포인터는 옵씨의 동작 전체에 관여하는 아주 중요한 포인터로, 이게 없으면 옵씨는 아무 것도 할 수가 없다. `isa` 포인터는 이 객체의 정체성을 나타내며, 이 객체의 클래스 객체를 가리킨다. === 클래스 객체 === 방금 '''클래스 객체'''라고 했다. 아니, 클래스면 클래스고 객체면 객체지, 도대체 클래스 객체란 뭐람? 자바나 C++로 객체지향을 시작한 사람들이 여기서 멘붕을 겪는다. 우리가 흔히 알기로는 클래스는 설계도이고, 이 설계도대로 공장에서 찍어낸 결과물이 객체이다. 하지만 옵씨에서는 클래스 그 자신도 객체이다. 그래서 우리가 어떠한 클래스의 인스턴스를 생성하면 그 인스턴스는 클래스 한정의 인스턴스 변수를 담고 `isa`가 클래스 객체를 가리키는 형태로 메모리에 할당이 된다. 예를 들어보자. 다음 코드는 `say`라는 메서드를 가진 `VARedFox` 클래스를 선언하며, 내부에는 `int` 형의 인스턴스 변수 하나를 가진다: {{{ // VARedFox.h @interface VARedFox: VAFox -(void)say; @end // VARedFox.m @implementation VARedFox [[include(틀:ProgrammingLanguage)]]