C++에서 Objective-C로                                        버전 2.3 ko                                   Pierre Chatelier    ...
Contents목차                                                                                                                ...
4.4.3 비공식 프로토콜 . . . . . . . . . . . .          .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   . ...
10 C++    고유의 특징                                                                                                          ...
소개  이 문서는 C++와 Objective-C 사이의 다리 역할을 하려는 의도로 작성되었습니다. 지금도많은 교재와 문서들이 Objective-C로 객체지향 모형을 가르치고 있지만, 고급 C++ 개발자들이이미 알고 있는...
1     Objective-C와 코코아먼저 Objective-C와 코코아(Cocoa)를 잘 구별해야 한다. Objective-C는 프로그래밍 언어이고,코코아(Cocoa)는 MacOS X(“맥 오에스 텐”으로 읽는다)의...
2       문법 개요2.1     키워드Objective-C는 C 언어를 포함한다. C++와 마찬가지로 C에서 허용하는 나쁜 기능들을 일부러쓰지 않고 잘 작성된 C 프로그램이라면 Objective-C로 컴파일할 수 ...
2.4.4    @encode자료형들 사이의 상호운용성(interoperability)를 위하여 Objective-C의 모든 자료형은 그것이 사용자 정의형이나 함수 프로토타입이라 할지라도 [4]에 나타나있는 형식에 따라...
object.doSomething();대신에[object doSomething];의 형태로 작성된 코드를 보고 있으면 쉽게 떠올릴 수 있는 생각이다. 그러나 사실 Objective-C는 C의 상위집합이기 때문에 함수를 ...
3       클래스와 객체Objective-C는 객체 지향 언어로 클래스와 객체를 다룬다. 이상적인 객체 모델과는 상당한 차이를보이는 C++과 달리, Objective-C는 엄격한 객체 모델을 사용한다. 4 예를 들면...
C++                               Objective-C   class Foo                               @interface Foo : NSObject   {     ...
C++   // Foo.h 파일                                 // Foo.cpp 파일   #ifndef __FOO_H__                           #include "Fo...
C++                                       Objective-C   class Foo                                    @interface Foo : NSOb...
• 매개변수(parameter)는 콜론 기호“:” 로 구분된다.   • 매개변수는 “:” 앞에 표시되어 있는 이름인 레이블과 결합될 수 있다. 이렇게 되면 레이블은     메소드 이름의 일부분으로서 메소드 이름을 수정하...
3.3.3   메소드 안에서 인스턴스 변수 접근하기C++에서와 같이, Objective-C 메소드는 현재 객체의 인스턴스 변수를 액세스할 수 있다. C++에서필요에 따라 사용할 수 있는 this->는 Objective-...
Objective-C에서는 모든 함수가 C 함수이기 때문에 오버로드될 수 없다. 오버로딩이 가능하려면 gcc에서 처럼 컴파일러에게 C9911 표준을 사용하라고 지정할 수 있어야 한다. 그러나, 메소드는C 함수와 다른 문...
3.3.5   멤버 함수에 대한 포인터: 선택자(Selector)Objective-C에서 메소드는 괄호와 레이블을 사용하는 독특한 문법을 갖는다. 이 문법으로 (C 언어의 표준적인) 함수를 선언하는 것은 불가능하다. 함...
Objective-C    @interface Slave : NSObject {}      -(void) readDocumentation:(Document*)document;    @end    // 10개의 slave...
Objective-C @implementation Foo -(void) f:(id)parameter //C 함수에서는 다음과 같다.                         //"void f(id self, SEL _...
3.4     메세지와 전송3.4.1   nil에게 메시지 보내기아무런 옵션 설정이 없으면 nil에 메시지를 보내는 것(메소드를 호출하는 것)이 기본적으로 허용된다. 이때 메시지는 그냥 무시된다. 이렇게 하면 널(nul...
-(void) forwardInvocation:(NSInvocation*)anInvocation    {      //만일 이 메소드로 이동했다면, 그 이유는 객체가      //호출(invocation) 메시지를 처리...
4       상속4.1     단순 상속Objective-C는 상속 개념을 잘 구현하고 있으나, 모호성 문제를 유발하는 다중 상속을 지원하지는 않는다(사실 Java, C# 등의 객체지향언어들도 이 문제 때문에 다중 상...
순수 가상 메소드의 개념(하위 클래스에서 반드시 재정의해야 하는 메소드)은 공식 프로토콜의개념을 통해 해결하였다(비고. 23쪽 4.4.1절).4.3.3   가상 상속사실상 Objective-C에는 다중 상속이 없기 때문...
Objective-C    @protocol MouseListener      -(BOOL) mousePressed;      -(BOOL) mouseClicked;    @end    @protocol Keyboard...
@protocol Slave @required //필수 부분 -(void) makeCoffee; -(void) duplicateDocument:(Document*)document count:(int)count; @opt...
엄밀히 말하면, 프로토타입에 관한 지식과는 별도로 비공식 프로토콜은 컴파일러에게는 쓸모가 없으며 이것이 객체의 사용을 제한하지 않는다. 그러나, 이 방법은 코드의 자체 문서화를통해서 API를 좀 더 읽기 쉽게 만들어준다...
C++에서 Objective-C까지 번역자료
C++에서 Objective-C까지 번역자료
C++에서 Objective-C까지 번역자료
C++에서 Objective-C까지 번역자료
C++에서 Objective-C까지 번역자료
C++에서 Objective-C까지 번역자료
C++에서 Objective-C까지 번역자료
C++에서 Objective-C까지 번역자료
C++에서 Objective-C까지 번역자료
C++에서 Objective-C까지 번역자료
C++에서 Objective-C까지 번역자료
C++에서 Objective-C까지 번역자료
C++에서 Objective-C까지 번역자료
C++에서 Objective-C까지 번역자료
C++에서 Objective-C까지 번역자료
C++에서 Objective-C까지 번역자료
C++에서 Objective-C까지 번역자료
C++에서 Objective-C까지 번역자료
C++에서 Objective-C까지 번역자료
C++에서 Objective-C까지 번역자료
C++에서 Objective-C까지 번역자료
C++에서 Objective-C까지 번역자료
C++에서 Objective-C까지 번역자료
C++에서 Objective-C까지 번역자료
C++에서 Objective-C까지 번역자료
C++에서 Objective-C까지 번역자료
C++에서 Objective-C까지 번역자료
C++에서 Objective-C까지 번역자료
C++에서 Objective-C까지 번역자료
C++에서 Objective-C까지 번역자료
C++에서 Objective-C까지 번역자료
C++에서 Objective-C까지 번역자료
C++에서 Objective-C까지 번역자료
C++에서 Objective-C까지 번역자료
C++에서 Objective-C까지 번역자료
C++에서 Objective-C까지 번역자료
C++에서 Objective-C까지 번역자료
C++에서 Objective-C까지 번역자료
C++에서 Objective-C까지 번역자료
C++에서 Objective-C까지 번역자료
C++에서 Objective-C까지 번역자료
C++에서 Objective-C까지 번역자료
C++에서 Objective-C까지 번역자료
C++에서 Objective-C까지 번역자료
C++에서 Objective-C까지 번역자료
C++에서 Objective-C까지 번역자료
C++에서 Objective-C까지 번역자료
C++에서 Objective-C까지 번역자료
C++에서 Objective-C까지 번역자료
Upcoming SlideShare
Loading in...5
×

C++에서 Objective-C까지 번역자료

4,116

Published on

Pirre Cha

0 Comments
10 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
4,116
On Slideshare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
232
Comments
0
Likes
10
Embeds 0
No embeds

No notes for slide

C++에서 Objective-C까지 번역자료

  1. 1. C++에서 Objective-C로 버전 2.3 ko Pierre Chatelier e-mail: pierre.chatelier@club-internet.fr Copyright c 2005, 2006, 2007, 2008, 2009 Pierre Chatelier 한글 번역 : 김빛나, 강영민, 박동규 Korean Translation : BitNa Kim, Young-Min Kang, DongGyu Park 원본은 다음 웹사이트에서 볼 수 있습니다 : http://pierre.chachatelier.fr/programmation/objective-c.php 최신 한글 번역본은 다음 웹사이트에서 볼 수 있습니다 : http://ivis.cwnu.ac.kr/tc/dongupak/152 이 문서는 프랑스어와 영어로도 볼 수 있습니다 This document is also available in French and English Ce document est aussi disponible en français et en anglaisWith special thanks to: For their attentive reading and many helpful comments, I would liketo thank Pascal Bleuyard, Jérôme Cornet, François Delobel and Jean-Daniel Dupas, whosehelp was important in making this work the best possible. Jack Nutting, Ben Rimmington andMattias Arrelid have also provided many feedback. Jonathon Mah has been particularly impliedin bringing a lot of very judicious corrections.They are not responsible of any mistake I could add after their reviewing. 1
  2. 2. Contents목차 2개요 51 Objective-C와 코코아 6 1.1 Objective-C의 간단한 역사 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 1.2 Objective-C 2.0 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62 문법 개요 7 2.1 키워드 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 2.2 주석 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 2.3 코드와 선언의 혼합 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 2.4 새로운 형식과 값 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 2.4.1 BOOL, YES, NO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 2.4.2 nil, Nil 과 id . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 2.4.3 SEL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 2.4.4 @encode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 2.5 소스 코드의 구성: .h와 .m 파일 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 2.6 클래스 이름에서 왜 NS를 사용하는가? . . . . . . . . . . . . . . . . . . . . . . . . . 8 2.7 함수와 메소드의 차이 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83 클래스와 객체 10 3.1 루트 클래스, id 형, nil 과 Nil 값 . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 3.2 클래스 선언 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 3.2.1 속성과 메소드 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 3.2.2 전치 선언 : @class, @protocol . . . . . . . . . . . . . . . . . . . . . . . . . 11 3.2.3 public, private, protected . . . . . . . . . . . . . . . . . . . . . . . . . . 12 3.2.4 static 속성 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 3.3 메소드 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 3.3.1 프로토타입과 호출, 인스턴스 메소드, 클래스 메소드 . . . . . . . . . . . . . 13 3.3.2 this, self, 그리고 super . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 3.3.3 메소드 안에서 인스턴스 변수 접근하기 . . . . . . . . . . . . . . . . . . . . 15 3.3.4 프로토타입 식별자와 시그너처, 오버로딩 . . . . . . . . . . . . . . . . . . . 15 3.3.5 멤버 함수에 대한 포인터: 선택자(Selector) . . . . . . . . . . . . . . . . . . 17 3.3.6 매개변수(parameter)의 디폴트 값 . . . . . . . . . . . . . . . . . . . . . . . 19 3.3.7 가변 개수의 인자 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 3.3.8 익명 인자 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 3.3.9 함수 원형(prototype) 한정어(modifier) - const, static, virtual, “= 0”, friend, throw . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 3.4 메세지와 전송 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20 3.4.1 nil에게 메시지 보내기 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20 3.4.2 알려지지 않은 객체에 메시지 위임 . . . . . . . . . . . . . . . . . . . . . . . 20 3.4.3 포워딩(forwarding): 알 수 없는 메시지 다루기 . . . . . . . . . . . . . . . . 20 3.4.4 하향 형변환(downcasting) . . . . . . . . . . . . . . . . . . . . . . . . . . . . 214 상속 22 4.1 단순 상속 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 4.2 다중 상속 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 4.3 가상성 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 4.3.1 가상 메소드 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 4.3.2 가상 메소드의 암묵적인 재정의 . . . . . . . . . . . . . . . . . . . . . . . . 22 4.3.3 가상 상속 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 4.4 프로토콜 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 4.4.1 공식 프로토콜 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 4.4.2 선택적 메소드 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24 2
  3. 3. 4.4.3 비공식 프로토콜 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 4.4.4 protocol 타입 객체 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26 4.4.5 분리된 객체를 위한 메시지 자격심사 . . . . . . . . . . . . . . . . . . . . . 26 4.5 클래스 카테고리 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27 4.6 프토토콜, 카테고리, 서브클래싱의 통합 사용 . . . . . . . . . . . . . . . . . . . . . 285 인스턴스화 29 5.1 생성자, 초기화 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29 5.1.1 할당과 초기화의 차이 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29 5.1.2 alloc과 init 사용하기 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29 5.1.3 올바른 초기화의 예 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30 5.1.4 self = [super init...] . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31 5.1.5 초기화 실패 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32 5.1.6 alloc+init으로 생성자를 “쪼개기” . . . . . . . . . . . . . . . . . . . . . . 33 5.1.7 디폴트 생성자 : 초기화 지정 . . . . . . . . . . . . . . . . . . . . . . . . . . 33 5.1.8 초기화 리스트와 인스턴스 데이터의 디폴드 값 . . . . . . . . . . . . . . . . 35 5.1.9 가상 생성자 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35 5.1.10 클래스 생성자 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35 5.2 소멸자 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35 5.3 복사 연산자 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36 5.3.1 고전적인 복제방식, copy, copyWithZone:, NSCopyObject() . . . . . . . . . 36 5.3.2 NSCopyObject() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37 5.3.3 더미 복제, 치환성, mutableCopy 와 mutableCopyWithZone: . . . . . . . . 386 메모리 관리 40 6.1 new 와 delete . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40 6.2 참조 횟수 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40 6.3 alloc, copy, mutableCopy, retain, release . . . . . . . . . . . . . . . . . . . . . 40 6.4 자동 해제 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41 6.4.1 autorelease의 중요성 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41 6.4.2 오토릴리즈 풀(autorelease pool) . . . . . . . . . . . . . . . . . . . . . . . . 42 6.4.3 다수의 오토릴리즈 풀 사용하기 . . . . . . . . . . . . . . . . . . . . . . . . 42 6.4.4 autorelease에서의 주의사항 . . . . . . . . . . . . . . . . . . . . . . . . . . 42 6.4.5 autorelease와 retain . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43 6.4.6 편리한 생성자, 가상 생성자 . . . . . . . . . . . . . . . . . . . . . . . . . . . 43 6.4.7 설정자 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45 6.4.8 접근자(Getters) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47 6.5 리테인 순환(retain cycles) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49 6.6 가비지 수집기 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49 6.6.1 finalize . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50 6.6.2 weak, strong . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50 6.6.3 NSMakeCollectable() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50 6.6.4 AutoZone . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 507 예외 처리 518 다중 쓰레딩 53 8.1 쓰레드-세이프티 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53 8.2 @synchronized . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 539 Objective-C 문자열 54 9.1 Objective-C의 유일한 정적객체 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54 9.2 NSString과 인코딩 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54 9.3 객체의 설명, %@ 포맷 확장, NSString을 C 문자열로 . . . . . . . . . . . . . . . . . . 54 3
  4. 4. 10 C++ 고유의 특징 55 10.1 참조 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55 10.2 인라인 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55 10.3 템플릿 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55 10.4 연산자 오버로딩 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55 10.5 프렌드 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55 10.6 const 메소드 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55 10.7 생성자에서 초기화 리스트 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5511 STL과 코코아 56 11.1 컨테이너 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56 11.2 반복자(Iterator) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56 11.2.1 고전적인 열거 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56 11.2.2 빠른 나열형 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57 11.3 함수자 (함수 객체) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57 11.3.1 셀렉터 사용하기 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57 11.3.2 IMP 캐싱 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57 11.4 알고리즘 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5712 Implicit code 58 12.1 Key-value 코딩 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58 12.1.1 원리 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58 12.1.2 차단 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59 12.1.3 프로토타입 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60 12.1.4 고급 기능 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60 12.2 프로퍼티 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60 12.2.1 프로퍼티의 활용 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60 12.2.2 프로퍼티의 서술 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60 12.2.3 프로퍼티 속성 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62 12.2.4 프로퍼티의 사용자 지정 구현 . . . . . . . . . . . . . . . . . . . . . . . . . . 63 12.2.5 프로퍼티에 접근하는 문법 . . . . . . . . . . . . . . . . . . . . . . . . . . . 63 12.2.6 고급 내용 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6413 동적성질 65 13.1 RTTI (런타임 형 정보) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65 13.1.1 class, superclass, isMemberOfClass, isKindOfClass . . . . . . . . . . . 65 13.1.2 conformsToProtocol . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65 13.1.3 respondsToSelector, instancesRespondToSelector . . . . . . . . . . . . 65 13.1.4 id형을 가진 강한 타이핑과 약한 타이핑 . . . . . . . . . . . . . . . . . . . . 66 13.2 런타임시 Objective-C 클래스의 조작 . . . . . . . . . . . . . . . . . . . . . . . . . . 6614 Objective-C++ 6715 Objective-C의 미래 67 15.1 블록(block) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67 15.1.1 지원과 사용사례 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67 15.1.2 구문 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68 15.1.3 환경의 캡쳐링 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68 15.1.4 __block 변수 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68결론 70참고문헌 71문서 개정내역 72색인 73 4
  5. 5. 소개 이 문서는 C++와 Objective-C 사이의 다리 역할을 하려는 의도로 작성되었습니다. 지금도많은 교재와 문서들이 Objective-C로 객체지향 모형을 가르치고 있지만, 고급 C++ 개발자들이이미 알고 있는 내용과 Objective-C의 개념을 비교하여 이해를 도와주는 문서는 없습니다. 이러한문제 때문에, 맥과 아이폰 프로그램을 처음 시작하는 사람들에게는 Objective-C 언어가 코코아프로그래밍을 도와주기는 커녕 오히려 개발을 어렵게 만드는 장애물처럼 보입니다: (참조. 6쪽 1).실제로 Objective-C가 가진 객체지향적 특성이 기존의 C++나 Java와 같은 언어들과는 차이가 매우큰데, 이런 이유가 코코아 프로그램을 익히는 데에 큰 진입장벽이 됩니다. 이런 이유로 저도 코코아 API에서 제공하는 여러 유용한 개념들을 익히는데에 시간이 다소 걸렸습니다. 저는 개발자가언어를 이해하지 못해 Objective-C를 포기하거나 그 기능을 잘못 사용하는 일이 없기를 바랍니다.이 문서는 완벽한 레퍼런스는 아니지만 빠른 길잡이 역할은 할 것입니다. 객체지향과 세부적인문법 개념에 관해서 더욱 자세한 설명을 원한다면 전문적인 Objective-C 지침서를 읽어보는 것이좋습니다[4]. C#과 Objective-C를 비교하는 별도의 문서도 필요할지도 모르지만, 객체지향성이 약한 C++보다는 C#이 훨씬 더 Objective-C와 가깝기 때문에 C# 개발자는 분명히 Objective-C를 더 빨리배울 수 있을 것입니다. 제가 보기엔, C#은 C++보다 나은 고급 개념을 가지고 있음에도 불구하고그다지 흥미롭지는 않는데, 이는 Objective-C가 단순하면서도 강력한 특성을 가지는데 비해 C#은 복잡한 접근법을 제공하기 때문입니다. 그리고 .NET보다는 코코아(Cocoa) API의 품질이 훨씬낫기 때문이기도 합니다. 그러나 이러한 개인적인 의견이 본 문서의 주제는 아닙니다. 5
  6. 6. 1 Objective-C와 코코아먼저 Objective-C와 코코아(Cocoa)를 잘 구별해야 한다. Objective-C는 프로그래밍 언어이고,코코아(Cocoa)는 MacOS X(“맥 오에스 텐”으로 읽는다)의 기본적인 프로그래밍 환경을 제공하는 클래스 집합이다. 이론적으로는 코코아 없이도 Objective-C를 사용할 수 있다. Objective-C를처리할 수 있도록 하는 gcc 처리기(front-end)도 존재한다. 그러나 MacOS X 에서 이 둘은 뗄 수없는 관계로, Objective-C가 제공하는 대부분의 클래스가 코코아의 일부이다. 정확히는 Apple사가 OpenStep 표준을 MacOS X용으로 구현하여 1994년에 발표한 것이 코코아인데, Objective-C로 만든 개발자 프레임워크(framework)로 구성되어 있다. OpenStep 표준을구현한 다른 예로는 GNUstep 프로젝트 [6]가 있는데, 무료이다. 이 프로젝트는 대부분의 유닉스시스템에서 이식이 가능해지는 것을 목표로 개발중인 상태이다.1.1 Objective-C의 간단한 역사프로젝트 착수 단계에서부터 개선, 표준화 및 공식 발표 사이에 시차가 있어, Objective-C 가탄생한 정확한 날짜를 말하기는 어렵다. 그러나, Objective-C의 조상언어(C와 Smalltalk언어)와“도전자” 사이의 대략적인 역사는 그림 1에서 볼 수 있다. 1972 1978 1980 1983 1989 1995 1998-99 2001 2005-07 Smalltak-72 Smalltak-80 C ANSI C C 99 C++ Standard C++ C++0x draft Objective-C Objective-C 2.0 Objective-C++ Java C♯ C♯2 (C♯3 to come) Figure 1: Java, C, C#, C++, Objective-C의 연대표 Smalltalk-80은 최초의 “진짜” 객체 지향 언어이다. 또한 C++와 Objective-C는 C 언어의 기능을포함하여 발전된 상위집합(superset)을 만든 두 갈래의 서로 다른 방식이다. C++가 매우 정적이며,실행 성능을 높이는 것을 목표로 하고 있다면 Objective-C는 구문 특성이나 동적 성질이 SmallTalk에 매우 가깝다. Java는 C++ 사용자를 대상으로 하지만, 그 객체모델은 Smalltalk에서 영감을 얻었다. 그래서 제목과 달리 이 문서는 많은 면에서 Java를 참조하였다. 마이크로소프트가 개발한C# 언어는 Objective-C의 직접적인 도전자라고 할만 하다. Objective-C++는 Objective-C와 C++를 합한 것이라 할 수 있다. 현재 사용이 가능한 상태이지만, 몇 가지 기능들이 아직 완벽하지 않다. Objective-C++의 목표는 Objective-C와 C++의 문법을혼용할 수 있도록 하여 이 둘의 장점을 최대로 살려 보려는 의도록 개발되었다 (비고. 67쪽 14절).1.2 Objective-C 2.0이 문서는 MacOS X 10.5와 나란히 발표된 Objective-C 2.0의 새로운 기능을 고려하여 갱신되었다. 이 새로운 기능들은 깊이 있는 내용의 기술적 개선인데, 개발자들이 느낄 수 있는 고수준관점에서의 수정은 다음과 같은 것들이다. • 가비지 수집기 : 비고. 49쪽 6.6절 • 프로퍼티 : 비고. 60쪽 12.2절 • 빠른 열거형 : 비고. 57쪽 11.2.2절 • 프로토콜을 위한 @optional이나 @required와 같은 새로운 예약어 : 비고. 23쪽 4.4절 • 실시간 Objective-C 라이브러리 특성의 개선 : 비고. 66쪽 13.2절앞으로 나올 절에서 이러한 특징들 하나 하나를 세부적으로 다룰 것이다. 6
  7. 7. 2 문법 개요2.1 키워드Objective-C는 C 언어를 포함한다. C++와 마찬가지로 C에서 허용하는 나쁜 기능들을 일부러쓰지 않고 잘 작성된 C 프로그램이라면 Objective-C로 컴파일할 수 있다. Objective-C에는 몇가지 개념과 관련된 키워드만 추가되었다. 기존 코드와의 충돌을 피하기 위해, 이러한 키워드는 @ 문자로 시작한다. 추가된 키워드는 다음과 같다: @class, @interface, @implementation,@public, @private, @protected, @try, @catch, @throw, @finally, @end, @protocol, @selector,@synchronized, @encode, @defs (애플사의 개발 문서 [4]에는 더이상 기록된 것이 없음). Objective-C 2.0은(비고. 6쪽 1.2절), @optional, @required, @property, @dynamic, @synthesize를 키워드로 추가했고, nil, Nil 값과 id, SEL, BOOL 형(type)과 YES와 NO의 부울형 값을 사용할수 있다. 마지막으로, 다음과 같은 몇 가지 키워드는 특별한 경우의 맥락 내에서만 사용되고, 그밖에서는 예약어가 아니다: in, out, inout, bycopy, byref, oneway (이것들은 프로토콜 정의할 때볼 수 있다. 비고. 26쪽 4.4.5절) getter, setter, readwrite, readonly, assign, retain, copy,nonatomic (이것들은 프로퍼티를 정의할 때 볼 수 있다. 비고. 60쪽 12.2절). 언어 자체의 키워드와 루트 클래스(root class, 비고. 10쪽 3.1절)인 NSObject(모든 클래스의 부모)를 통해 상속받은 몇몇 메소드는 헷갈릴 수 있다. 예를 들어, alloc, retain, release,autorelease라는 메모리 관리기능을 가지는 “키워드”처럼 보이는 명령은 사실 NSObject의 메소드들이다. super와 self (비고. 13쪽 3.3.1절) 는 키워드로 이해될 수도 있지만, 사실 self는각 메소드에 넘겨지는 숨겨진 매개 변수이며, super는 self를 다른 방식으로 쓰도록 컴파일러에게 요구하는 명령이다. 어찌 되었든, 거짓 키워드와 진짜 키워드 사이의 혼동이 보통의 프로그램개발에서 큰 문제가 되지는 않는다.2.2 주석Objective-C에는 /* . . . */와 //의 두 가지 주석을 모두 사용할 수 있다.2.3 코드와 선언의 혼합C++에서처럼, 명령 블록 중간에 변수 선언문을 삽입할 수 있다.2.4 새로운 형식과 값2.4.1 BOOL, YES, NOC++에서 boolean 타입은 bool인데, Objective-C에서는 BOOL 이고, YES나 NO로 설정할 수 있다.2.4.2 nil, Nil 과 id이 세 개의 키워드는 이후에 설명할 것인데, 간단히 언급하면 다음과 같다. • Objective-C에는 모든 객체에 대한 포인터 타입으로 NSObject *와 같은 역할을 하는 id형이 있다. 이것은 약한 형검사(weak-typing) 도구이다. • nil은 객체에 대한 포인터로 NULL 과 같다. 그러나 nil과 NULL은 서로 호환되지 않는다. • Nil은 클래스 포인터에 대한 nil이다. Objective-C에서는 클래스도 객체이다. 즉, 클래스는 메타 클래스(meta-class)의 인스턴스이다.12.4.3 SELSEL 타입은 어떤 클래스 인스턴스 객체와도 무관한 메소드 식별자를 뜻하는 실렉터(selector)를저장할 수 있디. 이 값은 @selector에 대한 호출을 통해서 얻을 수 있다. 실렉터는 기술적으로는 실제 함수 포인터가 아니지만, C언어에서 사용하는 함수(C++의 메소드)에 대한 포인터의일종으로 생각할 수 있다. 자세한 내용은 17쪽 3.3.5절을 참조하라. 1 이와 달리 C++의 클래스는 객체를 만드는 설계도로 설명할 수 있다. [譯註] 7
  8. 8. 2.4.4 @encode자료형들 사이의 상호운용성(interoperability)를 위하여 Objective-C의 모든 자료형은 그것이 사용자 정의형이나 함수 프로토타입이라 할지라도 [4]에 나타나있는 형식에 따라 ASCII로 인코딩할수 있다. @encode(a type )를 호출하면 주어진 자료형을 표현하는 C 문자열(char*)이 반환된다.2.5 소스 코드의 구성: .h와 .m 파일C++과 마찬가지로, 각 클래스의 인터페이스와 구현부에 해당하는 코드는 서로 분리하는 것이좋다. Objective-C는 헤더파일을 위하여 .h를, 코드가 들어가는 파일로 .m 파일을 사용한다. 그리고, .mm 파일은 Objective-C++를 위해 사용한다(67쪽 14절을 볼 것). Objective-C는 #include를 대체하는 #import 지시어(directive)를 사용한다. 모든 C 헤더 파일은 중복 포함을 방지하기위하여 컴파일 보호 장치를 사용해야 한다2 . 이러한 불편은 Objective-C의 #import를 사용하면자동적으로 해결된다. 다음은 전형적인 헤더와 구현파일의 예인데, Objective-C의 구문은 나중에자세히 설명하겠다. C++ // Foo.h 파일 // Foo.cpp 파일 #ifndef __FOO_H__ //이중 포함을 방지 #include "Foo.h" #define __FOO_H__ // ... class Foo { ... }; #endif Objective-C //Foo.h 파일 // Foo.m 구현 파일 //클래스 선언은 Java 키워드인 #import "Foo.h" //"interface"와 의미가 //다르니 혼동하지 말것 @implementation Foo @interface Foo : NSObject ... { @end ... } @end2.6 클래스 이름에서 왜 NS를 사용하는가?이 문서에 나타나는 거의 모든 클래스 이름은 NSObject 또는 NSString처럼 NS로 시작한다. 이유는 간단하다: 이 클래스들은 모두 코코아 클래스이고, 대부분의 코코아 클래스는 NeXTStep환경에서 처음 만들어졌기 때문에 NS로 시작한다. 프로그램 작성시 클래스가 어디서 온 것인지를 식별하기 위해 접두어를 사용하는 것이 일반적인 관행이다3 .2.7 함수와 메소드의 차이Objective-C는 단순히 “대괄호(꺽쇠)를 사용하여 함수를 호출”하는 언어가 아니다. 이런 생각은아래의 코드 2 #ifndef ... 와 같은 매크로를 사용한다 [譯註] 들어 게임개발에 널리 사용되는 cocos2d 클래스들은 CC를 접두어로 사용한다. 그러나 C++와 C#의 경우는 3 예를네임스페이스를 통하여 클래스 이름이 충돌나는 것을 방지한다 [譯註] 8
  9. 9. object.doSomething();대신에[object doSomething];의 형태로 작성된 코드를 보고 있으면 쉽게 떠올릴 수 있는 생각이다. 그러나 사실 Objective-C는 C의 상위집합이기 때문에 함수를 선언하고 구현하며 호출하는 문법은 C와 완전히 동일하다. 반면,C에서 존재하지 않는 메소드는 대괄호([ ])를 사용하는 특별한 구문법을 가지고 있다. 그런데,이러한 차이는 문법적으로 다른 것에 그치는 것이 아니라, 의미에 있어서도 차이를 갖는다. 자세한내용은 10쪽 3.2절에서 더 깊이 다룰 것이다: 이것은 메소드의 호출이 아니라 메시지를 보내는것이다. 이것은 그냥 단순히 학술적인 구분이 아니라 Objective-C의 메커니즘이 어떠한지를 드러내는 특징이다. 소스 코드 작성에 있어서는 별 차이가 없지만, 이 메커니즘은 더욱 동적인 특성을갖는다. 예를 들면, Objective-C는 실행중에 메소드를 추가하는 일도 가능하다 (비고. 66쪽 13.2절). 이런 구문은 읽기도 쉬운데, 특히 중첩된 호출문의 경우에 더욱 좋다 (비고. 13쪽 3.3.1절). 9
  10. 10. 3 클래스와 객체Objective-C는 객체 지향 언어로 클래스와 객체를 다룬다. 이상적인 객체 모델과는 상당한 차이를보이는 C++과 달리, Objective-C는 엄격한 객체 모델을 사용한다. 4 예를 들면, Objective-C에서는클래스도 역시 객체이며 동적으로 관리할 수 있다. 즉, 실행 시간에 새로운 클래스를 추가하고,이 클래스의 이름으로 인스턴스를 만들거나, 클래스에게 어떤 메소드를 가지고 있는지 물어보는등의 다양한 일들이 가능하다. 이것은 기본적으로 매우 “정적인” 특성을 가진 C++에 추가적으로 도입된 것에 불과한 RTTI5 (비고. 65쪽 13.1절)보다 훨씬 더 강력하다. 게다가 RTTI의 결과가컴파일러에 의존적이며, 이식성도 떨어지기 때문에 RTTI 사용을 권장하지 않는 것이 일반적이다.3.1 루트 클래스, id 형, nil 과 Nil 값객체지향 언어에서는, 개별 프로그램이 다수의 클래스의 모음을 사용한다. C++와는 달리, Objective-C는 루트(root) 클래스라는 것을 정의한다. 새로이 만들어지는 모든 클래스는 이 루트 클래스의자손이어야 한다. 코코아에서 루트 클래스의 이름은 NSObject인데, 이 클래스는 런타임 시스템(run-time system)6 을 지원하기 위한 방대한 양의 도구들을 제공한다. 루트 클래스 개념은 특별히Objective-C에서만 사용되는 용어가 아니다. 이것은 객체 모델과 관련된 것이다. C++는 이 개념을사용하지 않지만, Smalltalk과 Java는 사용한다. 엄밀히 말하면, 모든 객체는 NSObject 타입이어야 하고, 모든 객체에 대한 포인터는 NSObject*로 선언될 수 있다. 사실은 객체에 대한 포인터인 NSObject*를 대신해 간단히 id 형을 사용할 수있다. 이것은 임의의 객체를 가리킬 수 있는 (범용) 포인터를 선언하는 빠르고 간편한 방법이며, 정적 형검사 대신 동적인 형검사를 제공한다. 이것은 범용 메소드에서 느슨하게 형 검사를적용하는 데에 매우 유용하다. 7 객체에 대한 null 포인터가 NULL이 아니라 nil로 설정되어야함에도 유의하여야 한다. 이 두 값은 상호 교환되지 않는다. 보통 C 포인터는 NULL로 설정되지만 Objective-C에서 객체에 대한 포인터를 위하여 nil을 새롭게 도입하였다. Objective-C에서클래스는 객체(메타 클래스 인스턴스)이기도 하기때문에 클래스에 대한 포인터를 선언하는 것이가능한다. 그 null 값은 Nil이다.3.2 클래스 선언클래스 선언과 구현에 대한 Objective-C와 C++의 모든 차이를 딱 하나의 예로 보이기는 어렵다.구문법만으로는 개념적 차이를 알 수 없어 설명이 필요하다. 지금부터 그 차이를 차례차례 하나씩보이겠다.3.2.1 속성과 메소드Objective-C에서 속성은 인스턴스 데이터(instance data)라 불리며, 멤버 함수는 메소드(method)라 부른다. 4 C++는 friend 함수를 사용하여 객체 내부의 멤버에 손쉽게 접근하는 편의성을 제공하기도 하며, C 언어와의 호환성을 유지하기 위하여 함수나 메소드를 문법적으로 동일한 방식으로 호출하여 C 개발자들이 쉽게 C++ 개발자가 될수 있도록 하였다. 이러한 방식에 장점도 분명 있겠지만 객체지향적 개념을 훼손하는 C 언어의 특성을 배제하지 못하여다양한 문제를 일으키기도 한다. 또한 C++는 간략한 원칙보다는 다양한 상황에 대처하는 ad hoc한 문법들이 복잡하게포함되어 학습이나 활용이 매우 어려운 언어이다. [譯註] 5 Run-Time Type Information(런타임 형 정보)의 약자 [譯註] 6 런타임 시스템은 어떤 컴퓨터 언어로 작성된 프로그램이 실행되는 것을 지원하기 위해 설계된 소프트웨어를 뜻한다.런타임 시스템은 기본적인 저수준 명령어의 구현을 담고 있으며, 고수준 명령어들을 구현하고 형 검사(type checking),디버깅, 코드 생성, 최적화 등의 기능을 지원할 수도 있다. [譯註] 7 약한 형 결합 방식은 프로그래밍 언어에서 강한 형 결합(strong typing)의 반대 개념으로 사용되었는데, 데이터의 형변환을 컴파일러나 인터프리터가 내부적으로 수행하는 방식이다. 이 방식은 컴파일 타임에 에러를 검출하지 않기 때문에프로그램에는 좀 더 많은 주의가 필요하지만 객체의 동적인 생성과 사용에 매우 편리한 환경을 제공한다. [譯註] 10
  11. 11. C++ Objective-C class Foo @interface Foo : NSObject { { double x; double x; } public: int f(int x); -(int) f:(int)x; float g(int x, int y); -(float) g:(int)x :(int)y; }; @end @implementation Foo -(int) f:(int)x {...} int Foo::f(int x) {...} -(float) g:(int)x :(int)y {...} float Foo::g(int x, int y) {...} @endC++에서는 속성과 메소드가 클래스의 중괄호(brace, ‘{’와 ‘}’) 안에 함께 선언된다. 메소드 구현문법은 영역 결정 연산자(scope resolution operator, Foo:: )가 추가된다는 것 말고는 C의 함수와유사하다.Objective-C에서는 속성과 메소드를 중괄호내에 함께 섞어 놓을 수 없다. 속성은 중괄호 안에선언되며 메소드는 괄호 뒤에 나타난다. 메소드의 구현은 @implementation 블록에 자리잡는다. 이것은 C++와의 중요한 차이점인데, 일부 메소드를 인터페이스에 노출하지 않고 구현할 수있다는 점이다. 이것은 나중에 자세히 설명될 것이다. 간단히 이야기하자면, 불필요한 선언을제거하여 헤더 파일을 깔끔하게 정리할 수 있다. (불필요한 선언이라 함은 “private” 메소드를선언하거나 소멸자와 같이 드러나지 않게 재정의된 가상메소드 등은 굳이 인터페이스에 선언할필요가 없다는 뜻이다). 자세한 설명은 22쪽 4.3.2절을 참조하기 바란다. 인스턴스 메소드의 앞에는 마이너스 기호(“-”)가 붙고, 클래스 메소드 앞에는 플러스 기호(“+”)(비고. 19쪽 3.3.9절)가 붙는다. 이 기호는 UML 표기법과 비슷하기는 하지만 이것과는 전혀상관이 없으며 public이나 private을 구분하는 의미를 가지고 있지 않다. 8 매개변수의 형(type)은 괄호로 묶여지며 각각의 파라미터들은 “:”기호로 분리된다. 프로토타입의 문법에 대한 자세한설명은 다음 페이지의 13쪽 3.3.1절을 참조하라. Objective-C에서는 클래스 선언의 끝에 세미콜론이 필요없다. 또한 클래스를 선언하는 키워드로 @class가 아니고 @interface를 쓴다는 것에 유의하라. 키워드 @class는 전치(forward) 선언에서만 사용된다(비고. 11쪽). 마지막으로, 클래스에 인스턴스 데이터가 없는 경우의 빈 중괄호는생략할 수 있다.3.2.2 전치 선언 : @class, @protocol헤더 파일들 사이에 순환적인 의존성이 나타나는 것을 막기 위해, C 언어는 전치(forward) 선언을지원한다. 이 전치 선언은 특정한 클래스의 내구 구조는 알 필요가 없지만, 그러한 클래스가 존재한다는 정보는 필요할 경우 프로그램 작성자(코더)가 해당 클래스를 선언할 수 있도록 해준다.전치선언을 위하여 C++에서는 키워드로 class를 사용하는데, Objective-C에서는 @class를 사용한다. 프로토콜의 선언이 있을 것이라는 것을 예측할 수 있도록 키워드 @protocol을 사용하는것도 가능하다(비고. 23쪽 4.4절). 8 이 기호들은 다만 비정적(nonstatic) 또는 정적(static) 메소드임을 의미할 뿐이다. 모든 정적 메소드는 객체를 인스턴스화 시키지 않아도 사용할 수 있기 때문에 alloc처럼 클래스가 객체를 생성하기 위한 메소드나 싱글턴(singleton)객체의 메소드로 적합하다. [譯註] 11
  12. 12. C++ // Foo.h 파일 // Foo.cpp 파일 #ifndef __FOO_H__ #include "Foo.h" #define __FOO_H__ #include "Bar.h" class Bar; //전치 선언 void Foo::useBar(void) class Foo { { ... Bar* bar; } public: void useBar(void); }; #endif Objective-C // Foo.h 파일 // Foo.m 파일 @class Bar; // 전치 선언 #import "Foo.h" @interface Foo : NSObject #import "Bar.h" { Bar* bar; @implementation Foo } -(void) useBar -(void) useBar; { ... @end } @end3.2.3 public, private, protected객체 지향 모델의 주요한 특징 중에 하나로 데이터 캡슐화(encapsulation)이 있다. 이것은 코드의어떤 부분에서는 해당 데이터가 보이지 않도록 하는 것인데, 이렇게 함으로써 데이터가 훼손되는것을 막는다.9 9 이러한 캡슐화를 통해 객체 내부의 데이터를 안전하게 보호할 수 있으며, 이렇게 보호된 데이터가 훼손되지 않은값을 유지하고 있는 상태를 데이터의 ‘완결성(integrity)’라고 한다. 코드가 특정 데이터를 접근할 수 있는지 그렇지 아니한지를 그 데이터를 ‘볼 수 있다’ 혹은 ‘볼 수 없다’로 표현한다. 이러한 데이터 접근성을 ‘가시성(visibility)’라고도부른다. 데이터의 완결성을 유지할 때, 오류도 쉽게 찾을 수 있다. [譯註] 12
  13. 13. C++ Objective-C class Foo @interface Foo : NSObject { { public: @public int x; int x; int apple(); @protected: protected: int y; int y; int pear(); @private: int z; private: } int z; int banana(); -(int) apple; }; -(int) pear; -(int) banana; @endC++에서 속성과 메소드는 public, protected, private 범위 중 하나에 속할 수 있다. 이때 아무런 지정이 없다면 기본적으로 private(비공개) 모드이다.Objective-C에서는 인스턴스 데이터에만 public, protected, private 모드를 지정할 수 있는데, 아무런 지정을 하지 않으면 protected이다. 메소드는 모두 public 모드이다.10 하지만@interface 부분에서 선언하지 않았던 메서드를 @implementation 부분에서 구현하거나, 클래스카테고리 기호를 사용함으로써 private 모드를 흉내낼 수도 있다(비고. 27쪽 4.5절). 이러한 방식이 구현된 메서드를 외부에서 호출하는 것을 완전히 막을 수는 없지만, 메소드의 노출을 줄일수는 있게 된다. 선언을 하지 않고도 메소드를 구현하는 방식은 Objective-C의 특별한 특성인데,22쪽 4.3.2절에 설명되는 특별한 목적을 가지고있다. 상속은 public, protected, private으로 구분하여 할 수 없으며(C++에서는 가능하다), 오직public 방식의 상속만 가능하다. Objective-C의 상속은 C++보다 Java에 가까워 보인다(22쪽 4절).3.2.4 static 속성Objective-C에서는 (C++의 static과 같은) 클래스 데이터 속성을 선언할 수가 없다. 그러나 다른방식으로 동일한 일을 할 수는 있다: 그 방법은 구현 파일에서 전역 변수를 사용하는 것이다(이때 스코프(scope)를 제한하도록 static이라는 C 키워드를 사용할 수도 있다). 클래스는 (클래스메소드나 일반적인 메소드를 통해) 이 속성에 대한 접근자를 사용할 수 있으며, 이들 값에 대한초기화는 클래스의 initialize 메소드에서 할 수 있다(비고. 35쪽 5.1.10절).3.3 메소드Objective-C에서 메소드에 대한 구문법(syntax)은 일반적인 C 함수를 사용하는 구문법과 다르다. 이 절은 이러한 구문법을 설명하고 그 배경이 되는 메시지 전달 원리에 대해 몇 가지 정보를제공하려고 한다.3.3.1 프로토타입과 호출, 인스턴스 메소드, 클래스 메소드 • 인스턴스 메소드(일반적인 경우에 해당한다)인 경우 “−”가 앞에 붙고, 클래스 메소드((C++ 에서는 static으로 구현함)인 경우에는 “+”가 앞에 붙는다. 이 기호는 public, private를 구분하기 위한 UML 표기법과는 전혀 관계가 없다. Objective-C에서 메소드는 항상 public 이다. • 반환(return) 값이나 매개변수의 자료형(type)은 괄호로 묶어서 표시한다. 10 C++는 메소드나 인스턴스 데이터 모두 public, protected, private가 될 수 있다. [譯註] 13
  14. 14. • 매개변수(parameter)는 콜론 기호“:” 로 구분된다. • 매개변수는 “:” 앞에 표시되어 있는 이름인 레이블과 결합될 수 있다. 이렇게 되면 레이블은 메소드 이름의 일부분으로서 메소드 이름을 수정하게 된다. 이런 함수를 호출하는 경우는 코드를 읽기가 훨씬 쉬워진다. 사실, 레이블 사용은 체계적이어야 한다. 첫 번째 매개변수는 레이블을 가질 수 없는데, 메소드 이름이 바로 이 첫 번째 파라미터의 레이블이라 할 수 있다. • 메소드 이름을 속성 이름과 동일하게 사용해도 충돌이 생기지 않는다. 이것은 접근자(getter) 메소드를 만들때 아주 유용하다(비고. 47쪽 6.4.8절). C++ //프로토타입 void Array::insertObject(void *anObject, unsigned int atIndex) // "book" 객체를 "shelf" 배열에 사용한다 shelf.insertObject(book, 2); Objective-C 레이블 없음 (C++문장의 직역에 해당) //프로토타입 //"insertObject::"라는 메소드이름에서 콜론은 구분하는 역할 //C++의 스코프 연산자가 아니라는 점에 주의할 것 -(void) insertObject:(id)anObject:(unsigned int)index // "book" 객체를 "shelf" 배열에 넣는다(레이블 없음). [shelf insertObject:book:2]; 레이블을 가진 Objective-C 스타일 표현 //프로토타입. "index" 매개변수는 "atIndex"로 레이블 됨 //이 메소드는 이제 "insertObject:atIndex:"로 명명됨 //이 방식의 호출은 문장처럼 쉽게 읽을 수 있다. -(void) insertObject:(id)anObject atIndex:(unsigned int)index // "book" 객체를 "shelf" 배열에 넣는다(레이블 있음) [shelf insertObject:book:2]; //에러 ! [shelf insertObject:book atIndex:2]; //성공 대괄호 구문은 “shelf" 객체의 insertObject ‘메소드를 호출한다’가 아니라, "shelf" 객체에게insertObject라는 ‘메세지를 보낸다’로 읽어야 한다는 것에 유의하라. 이것이 Objective-C의 핵심적 특징이다. 우리는 모든 종류의 객체에게 아무런 메시지라도 보낼 수가 있다. 이때 객체가메세지를 처리할 수 없다면 이를 무시할 수 있다(예외가 발생하지만 프로그램이 중단되지는 않는다). 메세지가 전달된 시점에 이를 수신한 객체가 이 메시지를 처리할 수 있다면, 수신 메시지와일치하는 메소드가 호출될 것이다. 어떤 클래스가 메세지와 일치하는 메소드를 가지지 않았다는것을 안다면 컴파일러가 경고를 낼 수 있다. 이러한 경우에도 오류(error)로 간주되지 않는 것은포워딩(forwarding)이라는 기능 덕분이다(비고. 20쪽 3.4.3절). 만일 메시지를 받는 대상이 id형으로만 알려진 경우, 컴파일하는 도중에 경고가 발생하지는 않겠지만, 실행 시간에 오류가 발생할수 있다.3.3.2 this, self, 그리고 superObjective-C에는 메시지를 전달할 특별한 대상으로 self와 super가 존재한다. self는 현재 객체(C++에서의 this처럼)이며, super는 부모 클래스이다. this 키워드는 Objective-C에서 존재하지않으며, self로 대치되었다. 주의 : 사실 self는 키워드가 아니라, 모든 메소드에게 전달되는 숨겨진 매개변수(parameter)인데, 그 값이 현재 객체이다. self는 C++의 this 키워드와는 달리 그 값을 바꿀 수도 있다. 하지만이런 변경은 생성자에서만 쓸모가 있다(비고. 29쪽 5.1절). 14
  15. 15. 3.3.3 메소드 안에서 인스턴스 변수 접근하기C++에서와 같이, Objective-C 메소드는 현재 객체의 인스턴스 변수를 액세스할 수 있다. C++에서필요에 따라 사용할 수 있는 this->는 Objective-C에서는 self->로 쓴다. C++ Objective-C class Foo @interface Foo : NSObject { { int x; int x; int y; int y; void f(void); } }; -(void) f; @end void Foo::f(void) { @implementation Foo x = 1; -(void) f int y; //this->y와 구분이 모호함 { y = 2; //지역변수 y를 사용 x = 1; this->y = 3; //현재 객체의 y속성임 int y; //this->y와 구분이 모호함 } y = 2; //지역변수 y를 사용 self->y = 3; //현재 객체의 y속성임 } @end3.3.4 프로토타입 식별자와 시그너처, 오버로딩함수란 함수 포인터나 함수 기호로 사용하기 위해 참조할 수 있는 코드 조각이다. 더욱이 함수를식별하는 기호로 함수의 이름이 좋은 후보가 될 수 있지만, 오버로딩(overloading)을 사용할 경우에는 주의가 필요하다. C++과 Objective-C는 프로토타입을 구별하기 위한 서로 상반된 방식을사용한다. C++는 매개변수의 자료형에 근거한 방식을 사용하고, Objective-C는 매개변수 레이블에근거한 방식을 사용한다.C++에서는 매개변수의 자료형이 다르다면 서로 다른 두 함수가 동일한 이름을 가질 수도 있다.메소드를 사용하는 경우에는 const 옵션으로도 메소드를 구별할 수 있다. C++ int f(int); int f(float); //가능, float는 int와 다르기 때문 class Foo { public: int g(int); int g(float); //가능, float는 int와 다르기 때문 int g(float) const; //가능, const로 구분함 }; class Bar { public: int g(int); //가능, g(int)는 Foo::와 다른 Bar::에 있다 } 15
  16. 16. Objective-C에서는 모든 함수가 C 함수이기 때문에 오버로드될 수 없다. 오버로딩이 가능하려면 gcc에서 처럼 컴파일러에게 C9911 표준을 사용하라고 지정할 수 있어야 한다. 그러나, 메소드는C 함수와 다른 문법을 사용하고 있으며, 파라미터 레이블을 이용하여 구별할 수 있다. Objective-C int f(int); int f(float); // 에러 : C 함수는 오버로딩할 수 없다 @interface Foo : NSObject { } -(int) g:(int) x; -(int) g:(float) x; //에러: 이 메소드는 위의 메소드와 동일 // (레이플이 없다) -(int) g:(int) x :(int) y; //성공: 두개의 익명의 레이블이 있다. -(int) g:(int) x :(float) y; //에러: 이 메소드는 위의 메소드와 동일 // -(int) g:(int) x andY:(int) y; //성공: 두번째 레이블이 "andY"이다 -(int) g:(int) x andY:(float) y; //에러: 이 메소드는 위의 메소드와 동일 // -(int) g:(int) x andAlsoY:(int) y; //성공: 두번째 레이블이 "andAlsoY"로 // "andY"와 다르다 @end 레이블을 사용하여 메소드를 식별하는 방법은 아래의 표현된 것처럼 함수의 정확한 “이름”을표현하는데 유용하다. @interface Foo : NSObject {} //메소드 이름은 "g"이다 -(int) g; //메소드 이름은 "g:"이다 -(int) g:(float) x; //메소드 이름은 "g::"이다 -(int) g:(float) x :(float) y; //메소드 이름은 "g:andY:"이다 -(int) g:(float) x andY:(float) y; //메소드 이름은 "g:andZ:"이다 -(int) g:(float) x andZ:(float) z @end명백히 어떤 두 개의 Objective-C 메소드는 자료형(type)이 아닌 레이블(label)을 이용해서 구분한다. 이런 방식으로 Objective-C에서는 “멤버 함수에 대한 포인터”가 아니라 선택자(selector)를이용하여 메소드를 가리키게 된다12 . 여기에 대한 설명은 17쪽 3.3.5절에서 이뤄진다. 11 ANSI표준화 이후 크게 진전되지 않던 C의 표준 개정 작업이 1990년대에 활발히 진행되었는데, 90년대 후반의 개정작업들이 ISO/IEC 9899:1999에 정리되어 출간되었다. 이 표준을 따르는 C 규범을 C99라 부른다. [譯註] 12 함수 포인터를 부연 설명하자면 함수 포인터의 자료형(type)은 당연히 포인터 형이다. 함수 포인터가 일반적인 포인터와 다른 점은 변수의 주소 값이 아니라 함수의 주소 값을 저장한다는 것 뿐이다. 함수는 사실상 코드의 한 부분인데,여기서 코드라는 것은 프로그래머가 프로그래밍 언어를 이용하여 작성한 프로그램이 컴파일(compile)되어 메모리에 탑재될 수 있는 기계어 형태로 변환된 것을 의미한다. 함수가 시작되는 부분에 대응하는 코드가 메모리 공간의 어느 곳에자리를 잡고 있는지 알려주는 주소값을 저장하는 것이 바로 함수 포인터이다. C 언어는 함수 자체를 변수로 만들 수 없기때문에 함수 포인터를 통하여 포인터가 가리키는 곳의 함수를 실행시킬 수 있다. [譯註] 16
  17. 17. 3.3.5 멤버 함수에 대한 포인터: 선택자(Selector)Objective-C에서 메소드는 괄호와 레이블을 사용하는 독특한 문법을 갖는다. 이 문법으로 (C 언어의 표준적인) 함수를 선언하는 것은 불가능하다. 함수에 대한 포인터의 개념은 C와 Objective-C가모두 동일하지만, 메소드에 대한 포인터에 대해서는 두 언어에 차이가 있다.C++에서 메소드에 대한 포인터를 쓰기 위한 구문법(syntax)은 까다롭기는 하지만, C 언어의함수 포인터와 동일하다. 포인터 구문의 초점이 자료형(type)에 맞춰져 있다. C++ class Foo { public: int f(float x) {...} }; Foo bar int (Foo::*p_f)(float) = &Foo::f; // Foo::f에 대한 포인터 (bar.*p_f)(1.2345); //bar.f(1.2345) 호출과 동일함Objective-C에서는 메소드에 대한 포인터를 위해 새로운 자료형을 도입하였다. 이 “메소드의포인터”를 selector라고 부른다. 이 자료형이 SEL인데, 그 값은 (매개변수 레이블과 함께) 메소드의정확한 이름에 대한 @selector를 사용하여 계산한다. NSInvocation 클래스footnoteNSInvocation클래스는 객체나 응용 프로그램간에 메시지를 포워딩하거나 저장하는데 사용되는 클래스이다.NSInvocation 객체는 Objective-C의 메시지, 타겟, 선택자, 매개변수, 반환 값등 모든 요소를 포함할 수 있다 [譯註]를 사용하여 메소드를 호출할 수 있다. 대부분의 경우 (NSObject로부터 상속받은)performSelector: 계열의 유틸리티 메소드가 간편하기는 하지만, 다소 제약이 있다. 가장단순한 형태의 performSelector:로는 다음의 세 가지 예가 있다.-(id) performSelector:(SEL)aSelector;-(id) performSelector:(SEL)aSelector withObject:(id)anObjectAsParameter;-(id) performSelector:(SEL)aSelector withObject:(id)anObjectAsParameter withObject:(id)anotherObjectAsParameter; 이때 반환값(returned value)은 호출되는 메소드가 반환하는 값과 형태와 동일하다. 메소드의 매개변수가 객체가 아닌 (즉, float, int 등과 같이 값 형식일) 경우는 객체 형태로의 변환을 위해 보통 NSNumber 같은 래퍼(wrapper) 클래스를 사용해야 한다. 더욱 일반적이고 강력한NSInvocation 클래스를 사용할 수도 있다 (관련문서를 참조). 앞서 언급한 바와 같이 객체에 구현되어 있지 않은 메소드라 할지라도 이 메소드를 호출하는 것을 막을 방법은 없다. 사실 메소드는 메시지가 수용될 수 있는 경우에만 실제로 구동된다. 그러나 객체가 메소드를 모르는 경우라도 처리 가능한 예외가 발생할 뿐 응용 프로그램 자체가 중단되는 일은 없다. 게다가 객체가 어떤 메소드를 구동할 수 있는지를 확인하기 위해 respondsToSelector:를 사용할 수도 있다. @selector() 값은 컴파일시 계산되기 때문에 실행코드를 느리게 만들지는않는다. 17
  18. 18. Objective-C @interface Slave : NSObject {} -(void) readDocumentation:(Document*)document; @end // 10개의 slave 배열 "array[]"와 // 도큐멘트 "document"를 가정해 보자 //일반적인 메소드 호출방식 for(i=0 ; i<10 ; ++i) [array[i] readDocumentation:document]; // readDocument를 직접 호출하는 대신 performSelector를 사용해본다 for(i=0 ; i<10 ; ++i) [array[i] performSelector:@selector(readDocumentation:) withObject:document]; // 선택자의 형은 SEL이다. // 다음 버전은 이전과 달리 컴파일시에 @selector가 평가되므로 // 그다지 효율적이지는 않다. SEL methodSelector = @selector(readDocumentation:); for(i=0 ; i<10 ; ++i) [slaves[i] performSelector:methodSelector withObject:document]; // 객체 "foo"의 형이 미리 알려지지않은 (id)형일때 // 검사자체는 필수사항이 아니다. 그러나 만일 // 객체가 readDocumentation: 를 가지지 않은 경우에는 // 예외가 발생할 수 있으므로 이를 방지하는 것이 좋다. if ([foo respondsToSelector:@selector(readDocumentation:)]) [foo performSelector:@selector(readDocumentation:) withObject:document]; 이제 선택자는 매우 간단한 함수형 파라미터로 사용할 수 있다. 정렬(sorting)과 같은 범용알고리즘은 이러한 방식으로 쉽게 특화될 수 있다(비고. 57쪽 11.3절). 엄밀히 이야기 하자면, 선택자는 함수에 대한 포인터가 아니다. 선택자의 실제 자료형은 실행환경에 의해 메서드 식별자로 등록된 C 문자열이다. 클래스가 로드될 때마다, 그 클래스의 메소드가 자동으로 테이블에 등록되어 @selector()가 동작할 수 있게 된다. 이런 방법을 사용하기때문에 두 개의 선택자를 비교할 때 문자열을 비교하는 방식이 아니라 오히려 ==(값 비교)연산을이용해서 주소값이 같은지를 비교하는 방식을 사용할 수 있는 것이다. 메소드를 C 함수처럼 보고 선택자가 아니라 IMP13 자료형을 이용하여 다른 방식으로 주소를얻을 수도 있는데, 여기에 대해서는 57쪽 11.3.2절에서 간단히 설명할 것이다. 이 방식은 거의사용되지 않고 최적화 용도로 드물게 사용할 뿐이다. 예를 들어 가상 호출은 IMP가 아닌 선택자에 의해 처리된다. C++언어에서 지원하는 함수 포인터에 대한 Objective-C의 동등한 대응물은선택자(selector)인 것이다. 마지막으로 독자 여러분은 C++ 언어에 있는 this와 같은 역할을 하는 Objective-C 언어의 self를기억하고 있을 것이다. 이 self는 현재 객체를 저장하기 위하여 모든 메소드마다 숨겨두고 사용할수 있는 매개변수이다. 또 다른 숨겨진 매개변수 _cmd도 있다는 점에도 유의하기 바란다. 이 _cmd매개변수는 (객체가 아니라) 현재 메소드 자체를 말한다. (이 _cmd를 performSelector:와 함께활용하여 다음과 같에 매우 객체지향적인 재귀호출을 만들 수 있다. - 역자 추가 설명) 13 IMP는 일종의 함수 포인터인데, 반환값은 id 형이고, 매개변수로는 id, 선택자(SEL), 가변 인자 등을 받는다.예를 들면 typedef id (*IMP)(id self,SEL ,...);와 같은 형태로 선언된다. IMP는 Objective-C함수를 실행시간에 호출하기 위해 사용되는데, IMP 값을 획득하기 위해서는 다음과 같은 구문을 사용한다: IMP myImp = [myObjectmethodFor:mySel]; 18
  19. 19. Objective-C @implementation Foo -(void) f:(id)parameter //C 함수에서는 다음과 같다. //"void f(id self, SEL _cmd, id parameter)" { id currentObject = self; SEL currentMethod = _cmd; [currentObject performSelector:currentMethod withObject:parameter]; //재귀호출 [self performSelector:_cmd withObject:parameter]; //위의 문장과 동일 } @end3.3.6 매개변수(parameter)의 디폴트 값Objective-C는 함수나 메소드의 매개변수에 디폴트 값을 지정하는 것을 허용하지 않는다. 몇몇 파라미터가 선택적(optional)으로 사용된다면 각각의 선택 상황에 해당하는 모든 함수를 생성해야한다. 14 생성자의 경우, 지정 초기화(designated initializer) 개념을 사용해야 한다( 33쪽 5.1.7절).3.3.7 가변 개수의 인자Objective-C는 Objective-C는 인자의 개수가 가변적인 메소드를 사용할 수 있다. C에서처럼, 구문법은 마지막 인자로 “...” 사용하게 된다. 많은 코코아(Cocoa) 메소드가 이런 방식을 사용하기는하지만 크게 유용한 것은 아니다. 더 자세한 내용은 Objective-C 기술 문서를 참고하라.3.3.8 익명 인자C++에서는 어떤 함수의 원형(prototype)을 기술할 때, 매개변수에 대해 이름을 부여하지 않아도해당 매개변수의 자료형만 기술되어 있다며 함수의 특징을 규정하는 데에 충분하므로 문제가되지 않는다. 하지만, Objective-C에서는 이런 방식이 불가능하다.3.3.9 함수 원형(prototype) 한정어(modifier) - const, static, virtual, “= 0”, friend, throwC++에서 함수 원형(prototype)에 몇 가지 한정어(modifier)를 부가할 수 있다. 그러나 Objective-C에서는 이런 한정어를 사용할 수 없다. 사용할 수 없는 한정어 목록은 아래와 같다. • const : 메소드는 const로 설정할 수 없다. 결과적으로는 mutable 키워드도 존재할 수 없다. • static : 인스턴스 메소드와 클래스 메소드의 구분은 함수 원형 앞에 “-” 또는 “+”를 붙여 구별한다. static 메소드는 “+” 기호로 표시한다. • virtual : Objective-C 메소드는 가상이기 때문에 이런 키워드가 필요없다. C++의 순수 가상 메소드 는 공식 프로토콜(protocol)이라는 개념으로 구현한다(비고. 23쪽 4.4절). • friend : Objective-C에는 “프렌드(friend)” 개념이 없다. • throw : C++에서는 메소드가 몇 개의 예외만을 전송할 수 있도록 제한할 수 있지만, 이것은 Objective-C에서는 불가능하다. 14 C++는 디폴트 파라미터를 지정하여 매개변수가 없을 경우 디폴트 매개변수와 그 매개변수의 값을 자동으로 생성할수 있는 편의성을 제공하지만, 이것이 원치않는 값을 설정하여 찾기 힘든 버그를 생성할 가능성도 있다. [譯註] 19
  20. 20. 3.4 메세지와 전송3.4.1 nil에게 메시지 보내기아무런 옵션 설정이 없으면 nil에 메시지를 보내는 것(메소드를 호출하는 것)이 기본적으로 허용된다. 이때 메시지는 그냥 무시된다. 이렇게 하면 널(null) 포인터인지 아닌지를 검사하는 코드를작성하지 않아도 되기 때문에 코드가 매우 간략하게 정리된다. GCC에서는 옵션 설정을 통해 이런편리한 기능을 막아버릴 수도 있는데, 이것은 프로그램을 더욱 최적화하기 위한 것이다.3.4.2 알려지지 않은 객체에 메시지 위임위임(델리게이션)은 코코아(Cocoa)에 있는 사용자 인터페이스 요소들(테이블, outlines...)에서는매우 일반적으로 사용되는 개념인데, 미지의 객체에게 메시지를 보낼 수 있는 능력을 활용한다.예를 들어, 어떤 객체는 일부 작업을 조력자(assistant) 객체에게 위임할 수 있다. //assistant를 정의하는 메소드 -(void) setAssistant:(id)slave { [assistant autorelease]; //메모리 관리에 관한 설명 참조 assistant = [slave retain]; } //performHardWork 메소드는 델리게이션을 사용할 수 있다 -(void) performHardWork:(id)task { //assistant가 performHardWork 메소드를 모르기 때분에 //우리는 이 객체가 메시지를 처리할 수 있는지 미리 검사해야한다. if ([assistant respondsToSelector:@selector(performHardWork:)]) [assistant performHardWork:task]; else [self findAnotherAssistant]; }3.4.3 포워딩(forwarding): 알 수 없는 메시지 다루기C++에서는 객체가 구현하지 않은 메소드를 호출하는 경우에 코드가 컴파일되지 않는다. Objective-C에서는 조금 다른데, 객체에 어떤 메시지든 보낼 수 있다. 보내어진 메시지가 처리될 수 없는것이라면 그냥 무시해 버린다 (물론 예외(exception)는 발생한다). 15 뿐만 아니라, 이 메시지를무시하지 않고 다른 객체에 포워딩(forwarding)하는 일까지 가능하다. 컴파일러가 어떤 객체의 자료형(type)을 알고 있다면, 메시지를 보내는 일 - 즉, 메소드를 호출하는 일이 실패할 것인지 미리 알 수 있고, 경고를 발생시킬 수 있다. 하지만, 이것은 컴파일이불가능한 오류가 아니기 때문에, 이런 경우에는 해당 메시지를 처리할 대안을 선택할 수 있다.이러한 두 번 째 선택은 forwardInvocation: 메소드를 호출하는 방식으로 표현되는데, 이 메소드는 가장 최근에 발생한 메시지를 다른 곳으로 연결한다. 이것은 명백히 NSObject의 메소드인데,디폴트로는 아무런 일을 수행하지 않는다. 조력자 객체를 관리하는 새로운 방식은 다음과 같다. 15 구현되지 않은 메소드를 호출하려는 메시지까지 전달할 수 있는 특징은 Objective-C 프로그램을 매우 유연하게 만들어 주지만, 분명한 단점도 가지고 있다. 그 단점은 구현되지 않은 메소드를 실수로 호출할 경우 컴파일 시간에 검출되지않고 실행시에 비로소 알 수 있기 때문에 프로그래머의 세심한 주의가 요구된다는 것이다. [譯註] 20
  21. 21. -(void) forwardInvocation:(NSInvocation*)anInvocation { //만일 이 메소드로 이동했다면, 그 이유는 객체가 //호출(invocation) 메시지를 처리하지 못했기 때문이다. //이때 "anInvocation" 객체에 "selector" 메시지를 보내어 봄으로써, //해당 선택자를 얻어 다른 객체에 처리를 넘길 수 있다. if ([anotherObject respondsToSelector:[anInvocation selector]]) [anInvocation invokeWithTarget:anotherObject]; else //그래도 안 되면 부모클래스로 시도해보는 것도 잊지 말자 [super forwardInvocation:anInvocation]; } 어떤 메시지가 바로 처리되지는 않더라도 최종적으로 forwardInvocation:에서 처리될 수있다. 그러나 이런 경우에도 respondsToSelector:를 통해 검사를 하면 여전히 NO를 리턴할 것이다. 사실 respondsToSelector: 메커니즘은 forwardInvocation:의 작동할지 판단하는 것까지는고려하지 않았다. 호출 포워딩은 오류가 발생해야 할 때에 어떤 코드를 수행하는 것이기 때문에 좋지 않는 코딩방식이라 생각할 수도 있다. 그러나 사실 이러한 메커니즘을 아주 좋은 방식으로 활용할 수 있는데, 코코아의 NSUndoManager16 가 대표적인 예이다. 이 클래스는 매우 유쾌한 구문을 가능하게한다: 이 실행취소 관리자는 메소드에 대한 호출을 기록할 수 있다. 이 구문은 극히 예외적으로 유쾌한 문법을 허용하는데, 이 실행취소 관리자는 메소드 호출들이 있을 때, 자기 자신이 그 대상이아님에도 불구하고 모든 호출을 기록할 수 있다.3.4.4 하향 형변환(downcasting)하향 형변환은 C++에서 하위 클래스의 메소드를 호출할 때 필요한데, 부모 클래스 포인터만 알고있는 상황이라면, dynamic_cast를 이용하면 된다. Objective-C에서 이런 방식이 필요 없는데, 그이유는 Objective-C에서는 객체가 처리할 수 없는 메시지라도 얼마든지 보낼 수 있기 때문이다.17 그러나, 컴파일러 경고를 피하기 위하여, 단순히 객체 타입을 형변환(cast)시킬 수도 있다.Objective-C에는 명시적으로 사용하는 별도의 하향 형변환 연산자가 없기 때문에 C 언어의 전통적인 형변환 구문을 그대로 사용할 수 있다. //NSMutableString은 문자열의 교체가 가능한 //NSString의 하위 (문자열) 클래스이다. //"appendString:"은 NSMutableString에서만 지원된다. NSMutableString* mutableString = ...initializing a mutable string... NSString* string = mutableString;//NSString 포인터 변수내에 저장 //아래의 세 가지 메소드 호출은 모두 사용가능함. [string appendString:@"foo"];//컴파일시 경고 [(NSMutableString*)string appendString:@"foo"];//경고 없음 [(id)string appendString:@"foo"];//경고 없음 16 NSUndoManager 클래스는 실행취소(undo)와 재실행(redo) 연산을 위해 만들어진 범용 레코더이다. 17 하향 형변환을 이렇게 간단하게 할 수 있는 동적인 특성은 Objective-C가 가지는 큰 장점이다. 이러한 특성 때문에Objective-C에서는 모든 객체를 간단히 최상위 클래스 포인터인 id형으로 다룰 수 있다. [譯註] 21
  22. 22. 4 상속4.1 단순 상속Objective-C는 상속 개념을 잘 구현하고 있으나, 모호성 문제를 유발하는 다중 상속을 지원하지는 않는다(사실 Java, C# 등의 객체지향언어들도 이 문제 때문에 다중 상속을 지원하지 않고interface라는 방식을 사용한다). 이 때문에 발생하는 제약사항은 이 문서의 뒷부분에 설명하는다른 방법인 프로토콜, 클래스 카테고리등으로 대치한다(23쪽 4.4절, 27쪽 4.5절). C++ Objective-C class Foo : public Bar, @interface Foo : Bar //단일 부모를 통한 protected Wiz //상속만을 허용함 { //Wiz라는 부모의 기능에 대한 상속이 필요함. } //따라서 이 한계를 대체하는 기법이 필요하다 { } @endC++에서 클래스는 public, protected, private 모드 중 하나의 방식으로 부모 클래스로부터상속을 받을 수 있다. 메소드에서는 스코프 해석 연산자:: (Bar::, Wiz::)를 사용하여 슈퍼클래스를참조할 수 있다.Objective-C에서는 public 모드에서 하나의 클래스로부터만 상속받을 수 있다. Java 언어와유사하게 super 키워드(사실 Objective-C에서 super는 키워드가 아닌 메소드임)를 이용해서 슈퍼클래스를 참조할 수 있다.4.2 다중 상속Objective-C는 다중 상속을 구현하지 않지만 다른 개념인 프로토콜(비고. 23쪽 4.4절) 과 카테고리(비고. 27쪽 4.5절)를 채택하여 이 개념을 구현했다.4.3 가상성4.3.1 가상 메소드C++에서 가상 메소드는 메소드가 사용되기 전에 사용되기 때문에 "가상(virtual)"이라는 이름을붙이게 되었다. 가상 메소드는 컴파일러에게 "객체가 인스턴스화된 이후에 메소드의 구현부를가져오라"는 지시로 이해하면된다. 이러한 기법을 "사후 바인딩(late binding)" 또는 "동적 바인당(dynamic binding)"이라고도 한다. C++와는 달리 모든 객체가 동적으로 할당되는 특성을 가진Objective-C에서 모든 메소드 역시 가상이다. 따라서, 특별히 가상 메소드 선언을 위한 virtual키워드는 존재하지 않으며 존재할 필요도 없다.4.3.2 가상 메소드의 암묵적인 재정의Objective-C에서는 interface 절에 선언되지 않은 메소드를 implementation 절에서 구현하는 것도 가능한다. 이 기능을 이용하여 메소드에 대한 “숨기기” 기능을 어느정도 달성할 수 있지만,@private 개념을 완벽히 대체하지는 못한다(Objective-C에서는 메소드가 모두 public이라 C++의private 메소드는 존재하지 않는다). 그러나 이 방식은 인터페이스 선언을 간단하게 하는데 도움이된다. 선언을 하지않고도 구현부에서 구현하는 방법은 절대 나쁜 방법은 아니다. 심지어 Objective-C의 슈퍼 클래스에 있는 “잘 알려진” 메소드들도 이 기법을 사용한다. 루트 클래스 NSObject의 많은메소드는 암시적으로 재정의된다. 생성자 init (비고. 29쪽 5.1절), 소멸자 dealloc (비고. 35쪽5.2절), 뷰(View) 객체의 드로잉 메소드 drawRect:가 대표적인 예인데 이밖에도 많은 메소드가있다. 이 방식의 경우 비록 부모 객체에서 재정의된 부분이 무엇인가를 살펴보는 것이 까다롭기는하지만 이러한 재정의는 선언부를 작고 읽기 쉽게 만드는데는 도움이된다. 22
  23. 23. 순수 가상 메소드의 개념(하위 클래스에서 반드시 재정의해야 하는 메소드)은 공식 프로토콜의개념을 통해 해결하였다(비고. 23쪽 4.4.1절).4.3.3 가상 상속사실상 Objective-C에는 다중 상속이 없기 때문에 이와 관련된 문제도 없으므로, 가상 상속은Objective-C와는 관련이 없다.4.4 프로토콜Java와 C#은 인터페이스 개념을 이용하여 다중 상속을 지원하지 않음으로서 발생하는 문제를해결한다. Objective-C에서도 같은 개념이 사용되는데 이를 프로토콜이라 부른다. C++에서 이것에 해당하는 것이 추상 클래스이다. 프로토콜은 실제 클래스가 아니다. 메소드만 선언하고, 어떤데이터든지 가질 수 있는데, 이러한 프로토콜에는 두 가지 프로토콜이 있다. 이 프로토콜을 공식 프로토콜, 비공식 프로토콜이라 한다.4.4.1 공식 프로토콜공식 프로토콜은 해당 클래스에서 필수적으로 구현되어야하는 메소드들의 집합이다. 이것은 특정서비스에 필요한 모든 것을 처리할 수 있는지 확인하는 클래스에 대한 인증으로 보일 수 있다.클래스는 프로토콜의 갯수 제한을 받지 않는다. C++ class MouseListener { public: virtual bool mousePressed(void) = 0; //순수 가상 메소드 virtual bool mouseClicked(void) = 0; //순수 가상 메소드 }; class KeyboardListener { public: virtual bool keyPressed(void) = 0; //순수 가상 메소드 }; class Foo : public MouseListener, public KeyboardListener {...} //Foo는 반드시 mousePressed, mouseClicked, keyPressed를 구현해야함 //Foo는 마우스와 키보드를 위한 이벤트 리스너로 사용가능함 23
  24. 24. Objective-C @protocol MouseListener -(BOOL) mousePressed; -(BOOL) mouseClicked; @end @protocol KeyboardListener -(BOOL) keyPressed; @end @interface Foo : NSObject <MouseListener, KeyboardListener> { ... } @end //Foo는 반드시 mousePressed, mouseClicked, keyPressed를 구현해야함 //Foo는 마우스와 키보드를 위한 이벤트 리스너로 사용가능함C++에서 프로토콜은 추상 클래스와 순수 가상 메소드에 의해 구현된다. C++에서 추상클래스는데이터를 포함할 수 있기 때문에 Objective-C 보다 더 강력하다18 .Objective-C에서 프로토콜은 독특한 문법을 갖는다. 각진 괄호<...>를 사용한 문법은 C++템플릿(템플릿은 Objective-C에서 존재하지 않는다)과 아무 상관이 없다. Objective-C는 클래스 헤더부에 메소드 선언을 하지 않아도 프로토콜의 모든 메소드를 구현할수 있다. 이 경우에, conformsToProtocol:: 메소드는 NO를 리턴한다. 효율상의 이유로, conformsToProtocol:는 메소드별로 프로토콜의 적합성을 검사하지 않으며, 개발자가 명시적으로 선언한 것에 따른다. 그러나, 프로토콜의 메소드가 호출되는 경우 conformsToProtocol:의 부정적인 응답이오더라도 프로토콜의 메소드가 호출되기만 하면 프로그램은 올바르게 동작한다. 다음 문장은conformsToProtocol:의 프로토타입이다.-(BOOL) conformsToProtocol:(Protocol*)protocol//프로토콜 객체가 @protocol(protocol name) 호출에 의해 반환된다 공식 프로토콜을 준수하는 객체는 프로토콜 자체의 이름을 각진 괄호 사이에 추가될 수 있다.이 방식은 assertion에서 유용한다. 예를 들면 다음과 같다.//다음의 표준 코코아 메소드는 임의의 type (id) 형을 가지는//하나의 파라미터를 가질 수 있다. 그러나 NSDraggingInfo 프로토콜을//준수해야 한다.-(NSDragOperation) draggingEntered:(id <NSDraggingInfo>)sender;4.4.2 선택적 메소드클래스가 특정 서비스를 처리하도록하기 위해 프로토콜을 따르는 것이 바람직하겠지만, 모든 프로토콜을 사용하도록 강요하지는 않는다. 코코아 프레임워크에서 delegate 개념은 객체에 특별한기능을 부가적으로 수행하도록하기 위해 널리 활용된다. 이때 이 객체는 프로토콜의 모든 작업을수행하는것이 아니라 일부 작업만을 선택적으로 할 수 있다. 직관적인 해결책으로는 공식 프로토콜을 여러 개의 프로토콜로 분할한 후, 클래스로 하여금이러한 프로토콜 중 하나를 따르도록하는 방법이 가능할 것이다. 그러나 이 방법은 그다지 편리하지 않기 때문에 코코아는비공식 프로토콜(informal protocols)의 개념을 도입하여 이를 해결한다.Objective-C 1.0은 비공식 프로토콜을 사용할 수 있다(비고. 25쪽 4.4.3절). Objective-C 2.0부터는 새 키워드인 @optional과 @required을 도입하여 선택 메소드와 필수 메소드 사이의 구별을두었다. 18 위의 코드는 사실 Java 언어의 이벤트 처리를 담당하는 이벤트 리스너의 전형적인 구조이다. Java 개발자라면 Java언어와 비교하면 매우 쉽게 Objective-C와의 차이를 이해할 수 있을 것이다 24
  25. 25. @protocol Slave @required //필수 부분 -(void) makeCoffee; -(void) duplicateDocument:(Document*)document count:(int)count; @optional //선택 부분 -(void) sweep; @required //필수/선택 절을 구분할 수 있다 -(void) bringCoffee; @end4.4.3 비공식 프로토콜비공식 프로토콜(informal protocol)은 진짜 “프로토콜”이 아니며, 프로그램 코드에 아무런 강제적인 제약 조건도 만들지 않는다. 그러나 그것은 본래 “비공식(informal)”인데 코드의 자동 문서화를목표로한다. 비공식 프로토콜은 개발자가 응용 분야에 따라 메소드를 묶을 수 있도록 하며, 이를 통해 클래스들을 일관성있게 조직할 수 있도록 한다. 이 기능을 통해 클래스를 일관성있게 조직화시킬 수있다. 이런 이유로 비공식 프로토콜은 공식 프로토콜을 느슨하게 만들려고 만들어진 것이 아니라는것은 놀랄일이 아니다. 이를 위해 클래스 카테고리라는 새로운 개념이 사용된다(비고. 27쪽 4.5절). 앞서 살펴본 예제인 노예(slave) 클래스에 대해 “문서 관리”라는 서비스를 추가하는 것을 상상해보자. 이 서비스에서는 녹색, 청색, 적색 문서 서비스간의 차이가 있다는 가정을 함께 해보자.비록 이 클래스가 청색 문서만을 처리할 수 있더라도, Slave 클래스는 manageGreenDocuments,manageBlueDocuments 및 manageRedDocuments 3개의 공식적 프로토콜을 사용하는 것을 더 선호한다. Slave 클래스에 카테고리 DocumentsManaging을 추가하는 일을 하는 메소드를 선언하기위해, 이 클래스를 상속하는 수직적 확장대신 수평적 확장기능인 카테고리가 유용할 것이다. 이카테고리의 이름은 괄호안에 지정한다(보다 자세한 설명은 다음의 절을 참조하라 27쪽 4.5절). @interface Slave (DocumentsManaging) -(void) manageBlueDocuments:(BlueDocument*)document; -(void) trashBlueDocuments:(BlueDocument*)document; @end 어떠한 클래스라도 이 서비스와 관련있는 메소드를 선언하기 위해 DocumentsManaging 카테고리를 사용할 수 있다. @interface PremiumSlave (DocumentsManaging) -(void) manageBlueDocuments:(BlueDocument*)document; -(void) manageRedDocuments:(RedDocument*)document; @end 개발자는 코드를 브라우징하여 DocumentsManaging 카테고리를 볼 수 있다. 이에 따라 클래스가 몇 가지 작업에 유용한지 예측하여, 문서를 컨설팅하여 정확하게 작업을 확인할 수 있다. 소스코드를 확인하지 않더라도, 런타임시에 메소드가 존재하는지 조회하는 기능은 여전히 가능한다. if ([mySlave respondsToSelector:@selector(manageBlueDocuments:)]) [mySlave manageBlueDocuments:document]; 25
  26. 26. 엄밀히 말하면, 프로토타입에 관한 지식과는 별도로 비공식 프로토콜은 컴파일러에게는 쓸모가 없으며 이것이 객체의 사용을 제한하지 않는다. 그러나, 이 방법은 코드의 자체 문서화를통해서 API를 좀 더 읽기 쉽게 만들어준다. 카테고리는 또한 인스턴스 변수를 추가할 수 없다는 한계가 있으며, 이름 충돌을 방지하는기능이 없으므로 이 부분에서의 주의가 필요하다.4.4.4 protocol 타입 객체객체의 형태를 통해서 구현되는(또는 인스턴스 되는) 클래스와 같이, 프로토콜은 실행시에 Protocol*형으로 표현된다. 이러한 객체는 conformsToProtocol:과 같은 메소드의 매개변수로 사용될 수있다(비고. 65쪽 13.1.2절). 키워드 @protocol은 프로토콜을 선언하는데 사용되며, Protocol* 객체가 이것에 대한 참조객체의 이름으로 선언될 수 있다.Protocol* myProtocol = @protocol(protocol name ).4.4.5 분리된 객체를 위한 메시지 자격심사Objective-C의 동적 성질 때문에 분리된 객체들끼리도 쉽게 통신할 수 있다. 이 객체들은 서로다른 프로그램에 소속되어 있을 수 있지만, 공동의 작업을 위임받고 정보를 교환할 수 있다. 이제,공식 프로토콜은 어디서 왔던간에 이 객체가 주어진 서비스에 따른다는 것을 보증하는 완벽한방법이다. 공식 프로토콜 개념은 분리된 객체들이 보다 효과적으로 통신할 수 있도록 하기 위해몇몇 추가 키워드를 제공하고 있다. 이러한 키워드들이 in, out, inout, bycopy, byref, oneway 이다. 이 키워드들은 분산 객체에서프로토콜 정의의 외부에서만 적용할 수 있으며, 예약된 키워드가 아니기 때문에 편하게 사용할수 있다. 이 키워드는 프로토콜의 행동에 대하여 추가 정보를 추가하고 형식적 프로토콜 내부에 선언된메소드 원형 안에 삽입된다. 그들은 input 파라미터가 어느 파라미터이고, ouput 결과는 어떤것인지 지정하기 위하여 이용될 수 있다. 그들이 사본 또는 참고에 의해 사용되었는지 알려주는 것도가능한다. 그리고 메소드가 동기화되도록할지 아닌지를 알려주는 것도 가능하다.다음에 다양한 의미를 설명한다. • in 파라미터는 입력 변수이다. • out 파라미터는 출력 변수이다. • inout 파라미터는 두 가지 방식(입력과 출력)으로 사용될 수 있다. • bycopy 파라미터는 복사에 의해 전송된다. • byref 파라미터는 참조(복사가 아닌)에 의해 전송된다. • oneway 메소드(결과를 곧바로 예상할 수 없다)는 비동기 방식이다. 따라서 void를 리턴해야 한다. 예를 들어, 다음은 객체를 반환하는 비동기 메소드이다.-(oneway void) giveMeAnObjectWhenAvailable:(bycopy out id *)anObject;디폴트로 파라미터는 const 포인트의 경우 in으로 선택되며, 그외의 경우는 모두 inout으로 지정된다. inout 대신 in이나 out을 선택하는 것이 최적화에 유리하다. 파라미터를 전송하는 디폴트모드는 byref이며, 메소드는 기본적으로(oneway 제외) 디폴트로 동기된다.포인터가 아닌 값에 의해 파라미터가 전송될 경우 out, inout은 사용할 수 없고, in만 가능하다. 26

×