반응형
출처 : http://m.blog.naver.com/zparkx/100809974
오브잭티브-씨 래퍼런스 가이드 메모 정리
오브잭티브는 OOP. 즉 객채 지향중심으로 프로그래밍을 합니다.
C++에서 클래스의 내부 변수는 맴버변수라고 하지만 오브잭티브에서는 인스턴스 변수라고 합니다.
이것은 객채지향적 표현으로 프로티지(소유자, 혹은 재산) 라고 한다는점을 기역해둡시다.
C++에서 클래스의 맴버함수는 오브잭티브에서는 메소드(method)라고 부릅니다.(의미는 다른분야에도 사용되니 기역해둡시다)
보통 인스턴스 변수는 해당 객체의 액서스 메소드를 이용하여 접근합니다.
즉, get 함수와 put함수를 사용하게 되는데 보통 get 함수는 해당 인스턴스 변수와 같은 이름으로 합니다.
예>
@interface 클래스이름 : NSObject{
NSString *name //맴버 변수죠.
}
-(NSString*)name //인스턴스 메소드. 이렇게 맴버 변수와 인스턴스 메소드의 이름을 같게 합니다.
/*이것을 사용해서 '클래스이름' 클래스에 있는 name이 어떤값을 가지고 있는지 알고 싶다면 아래처럼 사용하면됩니다.*/
클래스이름 정화니; //클래스를 만드는 부분이죠.
...
[정화니 name]; //즉 클래스의 name맴버변수가 아니라 인스턴스 메소드를 사용합니다
예 끝>
오브잭티브에서는 인스턴스 변수와 같은 이름의 인스턴스 메소드를 가지는것이 가능합니다.
C++환경에서는 인스턴스 메소드인 (NSString*) name 은 char* name()과 같다고 볼수 있죠.
그리고 (NSString*) name처럼 ()괄호를 사용하는데 인자가 길어지고 많아진다면 복잡할수도 있으니 가독성을 위해
괄호를 한다고 보면 되네요. (아직 기능적으로 시험해보진 않았음)
<꼭 인스턴스 변수과 메소드가 같은 이름으로 정하지않아도 됩니다만 오브잭티브를 사용하는 사람들은 이런식의 스타일을 선호 하는것 같습니다>
오브잭트 포인터와 다이나믹 타이핑
오브잭티브는 동적인 언어입니다. 즉, 런타임시에 객체가 변할 수 있다는것이며 해당객체에 대한 메소드도 변할수 있습니다.
이것은 Apple에서 만들던 Dylan의 설명서를 보면 쉽게 이해할 수 있을겁니다(참고 자료는 아직 준비하지 못했습니다)
이렇게 동적이다보니 기존 스택 타이핑을 하던 객체에 대한 포인터를 사용하면 불편한 면이 있겠습니다.
(추가 설명. 매우 동적이다보니 객체에 대한 포인터를 정확히 내가 원하는 객체와 연결이 되느냐 하는것 같습니다)
그래서 나온 방법이 모든 객체를 두루 포인팅할 수 있는 타입을 새로 정의해 놓은것이 바로 id 입니다.
id 정화니 = [ [ 클래스이름 alloc] init ]; //클래스이름을 동적메모리활당(alloc) 하여 초기화(init)
이렇게 하면 정화니는 클래스이름이라는 타입의 객체를 가르키게 됩니다.
물론 static typing을 써서 다음과 비슷하게 할 수도 있습니다.
클래스이름 정화니 =...
이것은 C++의 보이드포인터(void*)와 비슷합니다. [보이드포인터에 대해 검색해보세요]
아무것도 가르키고 있지 않다면 nil이라는 이미 정의된 키워드를 NULL객체를 가르키기 위해서 사용가능합니다.
id 정화니 = nil;
id타입은 객체에 대해 어떠한 정보도 가지고 있지 않습니다. (단지 가르키고 있는 객체일뿐)
id로 선언된 변수는 어떤 객체라도 가르킬 수 있습니다.
이것은 기존 C++하던 사람에서 보자면 분명 문제를 일으키는 문제 입니다. 포인터를 사용하려면 가르킬 객체의 형태를 알아야 하는데 이것만으로는 알아낼 방도가 없기 때문이지요.
그래서 isa라는 인스턴스 변수를 묵시적으로 가지고 있게됩니다.
이것은 실제 그 객체가 어떤 형인지 기록해 놓는 것인데 이것을 이용하여 런타임시에도 사용이 가능합니다!
이런 메커니즘을 이용하여 다이나믹 타이핑이 가능합니다.
Object Messaging
오브잭트 메세지. 한 객체의 맴버 함수인 메소드를 호출하는것입니다.
보통 오브젝티브에서 해당 객체에 메시지를 보낸다라고 표현합니다.
C++에서는 반대로 맴버함수를 호출한다라는 표현을 사용하고 있죠.
바로 이 표현이 오브제티브의 OOP스타일과 C++의 OOP스타일의 주요 차이점인것 같습니다
물론 이것 말고도 다른점이 있지만 OOP를 구성하는 전체적인 그림의 차이는 바로 이 표현에서 보입니다.
Apple의 오브젝티브문서를 보면 SmallTalk과 기타 Objective-C문서를 보면 객체를 원으로 표현하고 그 객체에 메시지를 보내는것과 같은 객체의 네트워크로 프로그램을 표현하는 것을 많이 보실 수 있습니다.(도식화된 문서를 참고하세요)
메세지 호출법
한 객체에 메시지를 보내는 법. 다시 말하자면 그 객체의 메소드를 호출하는 방법은 이렇습니다
[객체 method]
즉, 이것을 C++스타일의 class.member 이거나 class->member 과 같습니다.
이것을 좀더 오브젝티브 답게 바꿔 보자면.
[receiver message]
즉 호출대상이 되는 객체는 보내는 메시지를 받는 것이 되므로 리시브(receiver)라고 하고 그 객체에 메소드를 메시지로 보내기 때문에 호출되는 메소드에 대해서는 message라고 표현합니다.
또 다른점은 메시지의 receiver의 앞과 message의 뒤에 [ , ] 의 괄호로 둘러 쌓아 놓는 것입니다.
이런 스타일은 참 낯설어 보이지만 익숙해지면 나름대로 깔끔하고 이 객체에 대해서 가독성이 올라갑니다.
C++스타일로는 포인터냐 아니냐에 따라 -> , . 연산자를 쓰면 연달아 쓸수 있습니다. 이렇게 쓰면서 객체라는 것을 알면서 사용할수 있습니다.
하지만 오브젝티브는 리시버를 쓰기 전에 '이 객체에 메시지를 보내야지!' 란 생각을 먼저해야하고 그 다음에 [ 를 쓰고 시작합니다.
그렇지 않으면 일단 뒤에까지 죽 썼는데 아차! 하고 다시 앞쪽에 괄호를 써야겠죠.
이 표현은 nesting(소굴) 될 수 있는데 그 경우엔 더 합니다
다음을 예로 듭시다
[ [ [ A child ] child ] child ]
여기서 child 가 객체들이 내부적으로 가지고 있는 어떤 객체를 반환한다고 할때 이런 경우엔 A앞에 괄호가 세개나 들어가므로 좀 writability(작성력)이 떨어지기는 합니다. (기존 프로그래머도 불편을 호소하기도 한다)
이제 매소드가 인자를 가질때는 아래와 같습니다.
[ myRect setWidth : 10.0 height : 15.0 ]
즉 myRect라는 어떤 객체에 폭과 높이를 셋팅하는 것입니다.
이런 표현도 낯설긴 하네요. setWidth까진 함수이름이고 : 다음 값을 넣는다고 생각했는데 htight부분이 낮설죠.
setWidth를 했으면 setHight라고 표현을 해줄것이지 말이죠.
이것의 매소드 정의하는곳을 볼까요?
-(void) setWidth : (float) width height : (float) height
우선 제일 앞의 -는 메소드가 인스턴스 메소드라는것을 의미 합니다.
즉 C++의 멤버함수와 같습니다.
그리고 이 매소드의 반환값에 대한 형태가 괄호에 (void) 로 되어 있고 그 뒤에는 메소드 이름 setWidth가 있죠.
그 다음부터는 메소드의 인자를 쓰게 되는데 우선 : 를 써서 인자를 구별합니다.
즉 (float) width는 이 메소드의 첫번째인자. 그의 형식은 float입니다.
그리고 두번째 인자를 쓰는데 딱히 구분지어주는 콤마는 없고 띄어쓰기로 구분지어지는것 같습니다.
즉 height는 2번째의 인자값의 라벨이라고도 할수 있겠네요.
이것을 C++식으로 바로 변경하면 아래와 같습니다
void setWidth(float width , float height)
그럼 'height : ' 가 대채 무엇인가 하는분도 있을겁니다.
즉 C++ 과 다르게 오브젝티브에서는 인자값에 라벨이 따릅니다.
고로 메소드를 사용함에 있어 어떤값을 넣어야 하는지 알려줄수 있고 이해하고 사용할수 있습니다.
그리고 - 는 대채 먼지 아직 이해가 가질 않네요.
설명하자면 아래와 같습니다.
- : 인스턴스 메소드
+ : 클래스 메소드
즉, +를 붙이면 C++의 클래스에서 static으로 선언한 것과 같은 효과 입니다. (static 맴버 함수 검색)
Polymorphism
오브잭티브는 OOP언어인만큼 Polmorphism(다향성 , 집단)과 땔수 없는 관계에 있습니다.(무엇을 의미하는지 모름)
C++과 마찬가지로 오브젝티브에서는 일단 객체가 다르다면 그들 사이에 같은 메소드를 가질 수 있습니다.
이런 형태도 오브젝티브에서는 다향성에 해당합니다.
왜 그런가 하면 "메시지 전달"이라는 점을 생각해보면 알 수 있습니다.
예를 들면 여러분의 코드가 display란 메시지를 Rectangle란 객체와 Cube라는 객체에 보낸다고 한다면 비록 두 객체가 display메소드를 가지고 있다고 해도 하는일이 다를수도 있습니다.
하지만 보내는 측에서는 id 로 두 객체를 가르킬수 있으며 아무런 문제 없이 display란 메시지를 보낼수 있습니다.
즉, 다른 객체가 같은 메시지에 대해서 반응하므로 이것도 Polymorphism이라고 합니다.
보통 C++에서라면 더 엄밀하게는 parametric polymorphism을 이야기 합니다.
즉 맴버 함수 이름은 같지만 그 인자는 다른경우입니다.
이런 경우도 오브젝티브도 지원합니다.
또한 오브젝티브는 C++처럼 부모 클래스(슈퍼클래스)의 매소드도 오버라이딩 할 수 있습니다.
하지만 오브젝티브는 연산자 오버로딩은 지원하지 못합니다
이것은 ad-hoc polymorphism이라고도 하는데 이것을 잘 이용하면 덧셈 부호를 행렬의 덧셈에 사용한다던가 하는식으로 상당하 간편하고 깔끔하게 코딩을 할 수 있었습니다.
아쉽지만 오브젝티브에서는 연산자 오버로딩은 지원하지 못합니다...
Dynamic Binding 혹은 Late Blnding
이렇게 오브젝티브에서 객체에 메시지를 보낼 수 있게 되는데 객체와 해당 메시지는 런타임시에 연결이 됩니다.
보통 오브젝티브 프로그래밍을 하면 객체는 id 형태로 받고 그것에 대해서 메시지를 보내기 때문에 컴파일시 그 객체의 형태가 구체적으로 무엇이 되는지 알 수가 없습니다. (즉 이런경우 에러를 찾기 힘들지도 모르겠다)
이런점은 장단점이 확연히 띄는데요 장점으로는 여러가지 형태를 담는 리스트나 큐를 만들기 상당히 쉽습니다.
하지만 컴파일시에 에러가 뜨지 않으니 런타임중 에러가 나면 에러가 난다고 생각하는곳을 예상하고 디버깅해야합니다.
그리거 성능의 문제가 되기도 합니다. 디버깅때 이뤄지는것이 아니라 런타임시 이뤄지기 때문에 그만큼 연산도 잡아먹죠.
3. Classes
이 챕터에서는 객체를 어떻게 만들어야하는지와 메소드를 어떻게 선언하는지 알아보겠습니다.(객채선언과 구현방법)
오브젝티브에서 객체를 정의하려면 그 클래스를 정의하면 됩니다.
즉 C++에서 클래스를 정의하는 것과 개념적으로 완전히 동일합니다.
컴파일러는 실제로 각 클래스에 대해서 클래스객체(CLASS OBJECT)란것을 하나만 만듭니다.
이 클래스 객체는 그 타입으로 만들어질 객체들을 어떻게 만들어야 할지에 대한 정보를 다 가지고 있으므로 factory object라고도 불리곤 합니다.
클래스 객체는 클래스의 컴파일된 버전입니다.
그 클래스 객체가 만드는 객체는 해당클래스의 instance라고 합니다.
실제 돌아가는 프로그램에서 주된 역할을 하게 되는 객체들은 사실 이렇게 런타임시에 클래스 객체를 이용하며 만들어지는 instance들입니다.(설명이 부족함 감이 없지않아 있지만 결국 추상적인 클래스를 논리적으로 보는지 아니면 런타임시의 실질적인 사물로 보는가에 따른 용어기 때문입니다.)
클래스의 각 인스턴스는 같은 메소드를 갖습니다.
즉 같은 인스턴스 변수를 가지게 되죠.
이때 각 객체들은 그 인스턴스 변수들을 자체적으로 내부에 가지게되지만 메소드들은 서로 공유하게 됩니다 (?)
크래스에 대한 코등 스타일은 전통적으로 그 이름을 대문자로 시작합니다. // class Apple
그리고 그 인스턴스의 이름은 보통 소문자로 시작합니다. // void setVal
상속( inheritance )
객채지향 언어인만큼 오브젝티브도 상속을 지원합니다.
클래스를 만들때 다른클래스와 유사한 부분이 있다거나 같은 부류인데 작은 개념이라 생각될때 다른 클래스의 인스턴스 변수와 매소드를 그대로 받아서 새로운 클래스를 만들 수 있습니다.
이때 다른 클래스로부터 받아들인 것을 그대로 쓰거나 아니면 목적에 맞게 수정도 가능합니다.
상속을 하면 한개의 루트 클래스 밑에 여러개 클래스들을 계층적인 형태로 연관지어놓을수 있습니다.
보통 Foundation 프레임 워크를 이용하여 만들어지는 코드는 루트로 NSObject를 위치시킵니다(Foundation ?)
루트 클래스를 제외하면 모든 클래스들은 그 상위. 즉 루트 클래스에 좀더 가까운 방향으로 하나의 슈퍼클래스를 가지게 됩니다
이때 하나의 슈퍼클래스라는 점에 주목해야 합니다.
오브젝티브는 다중상속을 지원하지 않습니다.
밑으로는 child class로 subclass를 가지게 됩니다.
정리하자면 다음과 같습니다.
C++ : 부모 클래스 , 자식 클래스
Objective-C : 슈퍼 클래스 , 섭 클래스
NSObject클래스
NSObject는 root클래스로서 슈퍼클래스를 가지지 않습니다.
Cocoa에서 이 클래스는 모든 클래스의 root 클래스가 됩니다.
이 클래스로부터 상속을 하면 런타임 시스템에서 지원을 받는 객채를 만들수 있습니다.
물론 이런 지원없이도 클래스를 만들수 있습니다.
인스턴스 변수들을 상속하기
한 클래스를 상속하면 C++의 경우와 마찬가지로 슈퍼클래스의 인스턴스 변수들도 모두 상속받게 됩니다.
그러므로 NSObject의 isa와 같은 인스턴스 변수는 NSObject를 상속한 모든 클래스에 자동으로 들어가게 됩니다.
메소드들을 상속하기
메소드 역시 인스턴스 변수들과 같은 방식으로 상속됩니다.
메소드 오버라이딩
C++과 마찬가지로 오브젝티브도 메소드 오버라이딩이 됩니다.
즉 슈퍼클래스로부터 상속받은 메소드를 뭔가 다른 식으로 바꾸고 싶다면 같은 메소드이름을 가진 메소드를 슈퍼클래스에서 정의함으로써 슈퍼클래스의 메소드 대신 invoke되게 할 수 있습니다 ( invoke ?)
물론 self 와 super 를 이용해서 오버라이딩된 슈퍼클래스의 메소드를 호출할수 있으니 상황에 맞게 프로그래밍 할수 있습니다.
단 섭클래스는 슈퍼클래스의 인스턴스 변수를 오버라이딩 할수 없습니다.
오버라이딩 하는것은 메소드에 한합니다.
Abstact class
이것은 C++의 abstract와 같습니다. ( 구조체? )
이런 예는 NSObject의 subclass를 정의하는 프로그램에선 그 subclass들을 사용합니다.
하지만 NSObject클래스로 정의된 인스턴스를 바로 쓰지는 않습니다.
abstact class는 abstact superclass라고 종종 부르기도 합니다.
클래스 type
클래스 정의는 어떤 종류의 객체에 대한 스팩입니다.
그러므로 클래스는 어떤 데이터 타입을 정의 하는것입니다.
타입은 클래스에 정의된 인스턴스 변수같은 데이터 구조뿐만 아니라 메소드같은것도 정의가 됩니다.
클래스 이름은 C 에서 type specifier를 쓸수 있다고 한곳에선 어디든지 쓸 수 있습니다.
즉 sizeof 연산자의 경우
int i = sizeof( Rect );
이런식으로 클래스 이름을 sizeof의 인자로도 전달할 수 있습니다.
Static Typing (정적 타이핑)
클래스를 id와 같은 동적 타이핑이 아닌 static typing을 이용해서 정의할 수가 있습니다.
이건 C++에서 클래스를 선언하듯이 하는 것입니다.
즉 다음과 같다고 할수 있겠네요.
Rect *myRect
또한 슈퍼 클래스 타입으로도 타이핑 가능합니다. (?)
Graohic를 상속받은 Rect클래스라면
Graphic * MyRect;
물론 이렇게 선언만 하는 것이야 변수 이름을 마음대로 할 수가 있으니 이런 예만으론 별 의미가 없습니다.
하지만 여기서 말하고자 하는 것은 Graphic 타입으로 선언된 변수가 Rect타입을 실제로 포인팅 할 수 있다는것입니다.
Type Introspection
이것은 런타임시 해당 인스턴스가 어떤 클래스의 인스턴스인지 알아내는것을 의미 합니다.
즉 런타임시 어떤 타입인지 무엇을 할수 있는지등 실행중에 변할수 있는 부분도 알아낼수 있는 메커니즘을 말합니다.
NSObject에는 이런 용도로 isMemberOfClass 와 같은 메소드가 정의 되어 있습니다.
if ( [ anObject isMemberOfClass : someClass ] )
....
라고 한다면 anObject라는 인스턴스가 someClass라는 클래스 타입인지 아닌지 알아낼수 있습니다.
즉 값이 YES 라면 anObject는 someClass 타입이라는 것입니다.
좀더 포괄적인것으로 isKindOfClass가 있습니다.
if ( [ anobject isMemberOfClass : someClass] ) // isMemberOfClass가 아니라 isKindOfClass인데 오타난것 같네요.
....
이것은 anObjec의 슈퍼클래스중 someClass가 있는가를 알아낼수 있습니다.
즉 someClass의 한 종류인지도 알수 있습니다.
isMemberOfClass의 경우 직접 슈퍼클래스의 형인지 아닌지를 알려주는데 후자의 것은 직접적인 슈퍼클래스가 아니여도 알아낼수 있습니다.
이외에도 주어진 객체가 특정 메시지 반응하는지 알아내는것등 introspection의 예는 많이 있습니다.
클래스 객체( Class Object)
클래스를 정의 하려면 다음과 같은 정보가 필요합니다.
- 클래스의 이름과 상속하는 슈퍼클래스 이름
- 인스턴스 변수의 템플릿
- 메소드 이름의 선언과 인자와 리턴 타입
- 메소드 구현
즉 C++과 비슷합니다.
클래스를 만들때 컴파일러는 해당 클래스에 대해서 한개의 class object만 만듭니다.
이 클래스 객체는 실제로 해당 클래스의 인스턴스는 아닙니다. 인스턴스를 만들기 위해 일종의 템플릿이라고 보면 됩니다.
하지만 클래스 객체를 위한 특별한 메소드를 가질수 있습니다.
이것은 클래스 메소드라고 부르는데 인스턴스 메소드와 개념을 잘 구별하기 바랍니다.
클래스 객체는 인스턴스 객체와 마찬가지로 그 상위 클래스로부터 클래스 메소드를 상속받습니다.
소스 코드에서 클래스 객체는 클래스 이름으로 표현합니다.
다음의 예는 Rect 클래스는 NSObject클래스에서 상속 받은 메소드를 이용해서 버젼 번호를 반환합니다
int versionNumber = [ Rect version ];
그런데 클래스 이름이 클래스 객체를 의미할때는 메시지 표현에서 리시버로 쓰였을때 뿐입니다.
다른 경우엔느 인스턴스나 클래스에게 클래스 id를 반환하라고 요청해야 합니다.
아래의 두 예는 모두 클래스 메시지에 반응을 합니다.
id aClass = [ anObject class ];
id rectClass = [ Rect class ];
이 예제에서 보듯이 클래스 객체도 id로써 타이핑 될 수 있습니다.
하지만 클래스 객체라는 것을 더 명확히 보이게 Class 타입으로 나타낼 수 있습니다.
Class aClass = [ anObject class ];
Class rectClass = [ Rect class ];
즉 모든 클래스 객체는 Class 타입 입니다.
이렇게 선언된 변수는 클래스 이름을 사용하는 것과 마찬가지 입니다.
그러므로 클래스 객체는 다이나믹하게 타이밍 할 수 있고 메시지를 받을 수 있고 다른 클래스로부터 메소드도 상속받을수 있습니다.
이 타입의 특징은 컴파일러에 의해서 만들어지고 클래스 정의를 통해서 만들어진 인스턴스 변수를 제외한 어떤 데이터 구조도 가지지 않으며 런타임시에 인스턴스를 만들기 위한 에이전트라는 것입니다.
참고
각 클래스에 대해서 컴파일러는 메타 클래스 객체라는 것을 만듭니다.
이건 마치 클래스 객체가 클래스의 인스턴스에 대해서 기술하듯이 클래스 객체를 기술하는 것입니다.
이 메타 클래스 객체는 런타임 시스템이 내부적으로만 사용합니다.
즉 여러분은 이 메타 클래스 객체를 쓸 수 없습니다. (뭥미.. 이해 불가..ㅠ)
인스턴스 만들기
클래스 객체를 사용하는 이유는 새 인스턴스를 만들기 위해서입니다.
다음의 코드는 Rect의 인스턴스를 만드는 코드입니다.
id *myRect;
myRect = [Rect alloc];
alloc 메소드는 Rect인스턴스의 변수들을 위해서 동적 메모리를 할당하는 메소드 입니다.
그리고 0(zero)로 초기화 합니다.
단. isa변수는 만들어진 새 인스턴스를 클래스에 연결시킵니다.(자동으로 형태구분없이 연결)
이렇게 만들어진 객체를 사용하려면 초기화 되어 있어야 합니다.
동적 메모리 할당과 동시에 초기화코드는 아래와 같습니다
myRect = [ [ Rect alloc] init ];
혹은 각각 사용할수도 있습니다
myRect = [ Rect alloc ];
[myRect init];
객체는 항상 이렇게 allocation되고 초기화 된후에 사용해야 합니다. (자세한 내용은 Objctive-c.pdf 챕터 10 참조)
Customization with Objective-C classes
오브젝티브는 OOP 언어니까 C++과 마찬가지로 상속을 지원합니다.
그럼 굳이 오브젝티브를 사용해야하는 목적을 생각하게되죠.
오브젝티브를 사용하는 목적은 개발시에 각 객체를 부품화 해서 사용이 용이하다는 점입니다.
물론 C++도 가능은 합니다만 OOP라는것이 개발자에 있어서 어떤 장점을 가져주는것인가에 대한 논란도 있다고 합니다.
(현재 C++은 RTTI (ReadTIme Type Information 실시간으로 객채의 정보) 를 지원하기 때문에 이 말은 더 이상 유효하지 않을수도 있다.
또한 추상클래스로 셀을 대표하는 클래스를 추상클래스로 만들고 가상 펑션을 만들어서 해결하면 될것이다.
하지만 같은 것을 하기 위해 오브젝티브에서는 얼마나 간단하게 처리를 하는지 염두에 두어 본다면 여전이 오브젝티브의 장점을 볼수 있다.)
그럼 오브젝티브가 커스터마이션을 어떻게 독특하게 하는지 알아보겠습니다.
NSMatrix라는 클래스가 Application Kit에 있습니다.
이 NSMatrix를 이용하면 전자계산기의 숫자판과 같은것을 행렬판. 혹은 표나 바둑판과 같은 배치를 이용할수 있습니다.
여기서 중요한점은 그렇게 만들어진 바둑판의 한 셀마다 들어갈 실제 객체들은 버튼이나 텍스트 필드등 무엇이든 다될수 있습니다.
보통 C++에서 이것을 접근한다면 NSMatrix를 abstract class(추상클래스) 로 만들어 놓고 그것을 사용할때는 서브 클래싱을 한 다음에 각 바둑판의 한칸을 의미하는 셀을 만들도록 메소드를 구현할것입니다.
용도에 따라서 각 셀이 가르켜야할 객체가 버튼이 될수도있고 다른것도 될수가 있으므로 이 클래스를 사용하는 사람들은 반드시 서브 클래싱을 해서 만든 객체가 본인들이 원하는 셀 타입을 지원하는 것인지 알아야 합니다.
변수 , 클래스 객체 (class object)
인스턴스변수와 클래스변수에 대해 알아보겠습니다.
객체에 대한 클래스를 정의할때 인스턴스 변수를 만들어 놓을 수 있습니다.
이렇게 만들어진 클래스의 객체들은 각자가 다 이런 인스턴스 변수를 가지고 있죠.
@interface myClass{
int name;
}
myClass classA , classB;
즉, C++과 같으며 classA와ClassB는 자체내에 int형의 name이라는 변수를 가지고 있고 서로 다른 독립적인것이 됩니다.
하지만 이런 인스턴스 변수에 대응하는 클래스 변수가 없습니다.
무슨말이냐 하면 class를 정의할때 초기화된 내부적인 데이터 struct(스트럭쳐)가 있을 뿐입니다.
이렇게 선언되면 name을 어떻게 변경할 방법이 없습니다.
클래스는 자체적으로 인캡슐화되어 있기 때문에 클래스의 함수가 없는이상 변경할수 없죠.
@interface myClass{
{
static int ex_class_var;
}
이렇게 static 타입으로 정의를 하게 되면 그 변수는 해당 클래스 내로 scope 가 제한되며 그 클래스 내에만 존재하는것입니다.
이렇게 선언된 static 변수는 상속이 되지 않습니다.
그러므로 모든 인스턴스 객체들은 이 변수의 내용을 공유하게 됩니다. (?)
클래스 객체 초기화 (INITIALIZE)
인스턴스 객체를 할당하기 위해서 사용되지 않는 경우엔 인스턴스로 초기화 되어야 합니다.
여러분이 만드는 클래스 객체를 할당하진 않더라도 오브젝티브는 프로그램이 클래스 객체를 초기화 할 수 있는 방법은 제공합니다.
즉 클래스가 static 이거나 global 변수들을 사용한다면 initallize 메소드를 이용하면 됩니다.
※ +initalize 메소드는 클래스 메소드로써 클래스 객체를 초기화 할때 사용합니다.
-init 메소드는 인스턴스 메소드로써 인스턴스 객체를 초기화 할때 사용합니다.
+ - 를 주목하시기 바랍니다
예를들어 클래스가 자신의 이느턴스를 관리하기 위해 만들어지는 인스턴스에 대한 리스트를 가지고 있다면
그 리스트는 initialize메소드에서 초기화 하면 됩니다.
runtime 시스템이 initalize메시지를 클래스 객체에 보냅니다.
즉 , 그 클래스가 다른 메시지를 받기전과 그 클래스의 슈퍼 클래스가 initalize 메시지를 받은 후에 runtime 시스템이 보내는것입니다.
그래서 실제 사용되기전에 초기화 할수 있습니다.
만약 이런 초기화가 필요하지 않는다면 굳이 여러분이 initalize를 구현할 필요는 없습니다.
아래것처럼 클래스 상속에 따른 메시지 전달을 합니다.
즉, 어떤 클래스에 대해서 initalize메소드를 구현하지 않았다면 그 클래스에 대한 initalize메시지는 그 슈퍼 클래스에 전달됩니다.
비록 그 슈퍼클래스가 이미 initalize 메시지를 받았더라도 말입니다.
그러므로 그 슈퍼 클래스는 초기화가 단 한번만 수행되도록 만들어져야 합니다.
초기화를 한번만 하려면 아래와 같이 하면 됩니다
+(void)initialize{
static BOOL initialized = NO;
if ( initialized == NO) {
// 초기화 코드를 기입
initialized = YES;}
}
여기서 주의할 점은 runtime 시스템이 이 initialize 메시지를 모든 클래스에 보낸다는겁니다.
즉 , 클래스 상속을 염두헤 두고 현재 만드는 인스턴스에 대해서만 보내는게 아니라 한 클래스의 슈퍼클래스에도 보냅니다.
그러므로 여러분이 initialize 메소드를 구현할 때 그 슈퍼 클래스에 메시지를 initialization메시지를 전달하면 안됩니다.
이것은 인스턴스를 초기화 할때 쓰는 init와는 사뭇 다릅니다.
루트 클래스의 메소드
루트클래스. 즉 클래스의 상속관계에서 제일 높은 위치. 자신의 조상클래스를 가지지 않는 클래스는 슈퍼클래스 입니다.
클래스 객체가 어떤 메시지를 받았을때 그것을 처리할수 있는 클래스 메소드가 없다면
런타임 시스템은 그것을 처리해줄 수 있는 인스턴스 메소드가 있는지 알아보게 됩니다.
클래스 객체가 수행할 수 있는 유일한 인스턴스 메소드는 루트 클래스에 정의된 메소드 뿐입니다. (?)
단. 받은 메시지를 처리해 줄 수 있는 클래스 메소드가 없을때 그렇다는 겁니다.
다시 정리하자면...
받은 메시지를 처리할 클래스 메소드가 없을때
클래스 객체는 루트 클래스 메소드 중 그것을 처리할 수 있는게 있는지 찾아봅니다.
그리고 처리할수 있는 메소드가 있을경우 그 루트 클래스 메소드를 수행하게 됩니다.
즉, 상속 관계에서 상위 클래스를 타고 올라가서 최종적으로 ROOT CLASS의 메소드를 찾게 되는 것입니다.
소스 코드에서 클래스 이름
소스 코드에서 클래스 이름은 다음의 두가지 용도로만 사용이 가능합니다.
1. 객체를 선언하기 위한 type 이름
Rect * anObject
참고로 인스턴스만 이렇게 static 하게 사용할수 있습니다.
클래스 객체는 이렇게 할 수 없습니다.
클래스 객체는 class 라는 type specifier를 이용하여 선언 합니다.
2. 메시지 표현에서 메시지의 리시버로 사용되어 클래스 객체를 지칭하기 위해서.
바로 이 경우에만 클래스 이름이 클래스 객체를 지칭합니다.
다른 예재에서는 클래스 객체가 그 id 를 드러내도록 클래스 메시지를 보내서 클래스 객체를 지칭하는 값을 알아내야 합니다.
다음의 예는 isKindOfClass: 메시지에 Rect 클래스를 인자로 전달하는 방법 입니다.
if ( [ anObject isKindOfClass : [ Rect class ] ] )
....
Rect를 인자로 사용하는 것은 않됩니다. (?)
클래스 이름은 메시지의 리시버로만 사용할 수 있습니다.
만약 컴파일시에 클래스 이름을 알아 낼 수 없고 런타임시에 그에 대한 스트링을 알고 있다면
NSClassFromString 함수가 클래스 객체를 반환해 줍니다.
NSString * className;
....
if( [ anObject isKindOfClass : NSClassFromString ( className ) ] )
....
만약 전달된 클래스 이름이 제대로 된 클래스 이름이 아니라면 nil을 반환합니다 ( NULL )
클래스 이름은 global 변수와 함수 이름들과 같은 namespace 내에 존재합니다.
그러므로 클래스와 global 변수는 같은 이름을 가질 수 없습니다.
Class 이름은 오브젝티브에서 global visibility 를 보이는 유일한 이름입니다.
Class 정의
이 부분은 실제로 오브잭티브에서 클래스를 어덯게 만드는지 설명합니다.
오브잭티브의 클래스는 다음의 두가지 파트를 이용해서 정의 합니다.
@interface
한 클래스의 메소드와 변수를 선언하고 어느 슈퍼클래스로부터 상속을 받는지 기입합니다.
c++클래스 구조의 정의와 비슷합니다.
보통 .h파일에 정의를 합니다.
@implementation
클래스의 메소드 부분을 정의 하는 부분입니다.
보통 .m에서 구현합니다.
컴파일러에서는 .h 나 .m 이나 어디서 구현하건 상관하진 않지만 보통 가독성을 좋기 하기 위해선 분리해주는게 일반적입니다.
이부분은 c++과 비슷하므로 특별한 설명을 하진 않겠습니다.
클래스 정의는 @interface 와 @end 사이에서 합니다.
@interface ClassName : SuperClass {
변수 등등 ...
}
인스턴스 메소드 함수 선언 부분
@end
메소드를 작성하는 것은 다음 형식을 따릅니다.
- (리턴타입) 메소드이름 : (인자값의 매개변수 타입) 인자값 두번째인자값의메소드 : (인자값의 매개변수 타입) 인자값;
ex>
-(int) 철수의키는 : (int)철수키 이고영희의키는 : (int)영희키; //선언
[철수의키는:185 이고영희의키는160] //사용
자 그럼 인자값이 없는 경우는 다음 형식입니다.
-(리턴타입) 메소드이름; // 뭐 간단하죠.
그렇다면 인자 값이 있다면?
-(리턴타입) 메소드이름 : (인자값의 매개변수 타입) 인자값; // 뭐 다를게 있나요.
타입을 괄호로 둘러싼다는점이 특이하죠.
그렇다면 위에도 언급했지만 타입을 2개 이상 쓰는 방법을 다른방법으로 설명한다면
-(void) setWidth: (float) width height: (float) height;
복잡해보이죠. 구분이 어떻게 지여지고 띄어쓰기는 해야 하는지 말이죠.(단어 단위로 인식하기 때문에 신경쓸필요는 없을것 같습니다)
이것을 사용할때는 다음과 같습니다.
id *myRect = [ [ Rect alloc ] init ] ;
[ myRect setWidth: 10.0 height: 20.0 ];
즉 전달하는 인자 자체에도 라벨을 같은것을 붙입니다.
어떻게 보면 C++ 보다 불편할수도 있습니다.
하지만 인자값이 뭐였는지 자꾸 찾는분들이라면 이런식으로 상당히 도움될것입니다.
다음은 인자의 갯수가 정해지지 않은 경우 입니다.
이 경우는 ellipsis로 나타냅니다 (ellipsis : 생략 )
- makeGroup : group, ... , ... ; // ( 사용방법은 않나와 있네요.. ; )
인터페이스 파일 임포트 ( import )C++의 #include와 비슷합니다.
오브잭티브에서는 #include 대신 #import를 사용합니다.
#import < 헤더 >
단. 다른점이라면 한번만 포함시켜준다는것입니다.
즉, #pragma once를 명시해준 것과 같습니다.
다른 클래스 언급하기
앞에 예와 같이 하면 ItsSuperClass.h 에 있는 모든 클래스를 import하는 것이 됩니다.
만약 이 인터페이스 파일에 선언되지 않은 클래스를 쓰려면 그 클래스에 대한 인터페이스 파일도 import 하던가 아니면
@class directive를 써서 어딘가 선언되어 있다고 컴파일러에게 알려주면 됩니다.
@class ClassA_name
사용예는 다음과 같습니다.
@class Rect , Circle;
이 에에서는 Rect 와 Circle이 클래스라고 컴파일러에게 알려주는 것입니다.
이것은 C++의 extern을 써서 다른 파일 어딘가에 그에 해당하는게 있다라고 명시해두는것과 같습니다.
이렇게 하면 굳이 해당 클래스의 인터페이스 파일을 import하지 않아도 됩니다.
하지만 정말 그런 클래스의 메소드를 사용할때는 반드시 해당 인터페이스 파일을 import해야 합니다
이것의 장점은 서로간의 cyclic dependency (순환 종속)을 가지는 경우에 있어도 컴파일 할수 있다는것입니다.
즉!
a.h 엔 class A 에 대한 선언이.
b.h 엔 class B 에 대하 선언이 있습니다.
그리고 a.m과 b.m도 있습니다.
그런데 각 코드에서 서로 사용하게 된다면
컴파일시 A 를 컴파일 하려면 B를 먼저 컴파일해 놔야 하고.
B를 하자면 A를 해 놔야 합니다.
그러므로 cyclic dependency가 생겨서 결국 컴파일을 못하게 됩니다.
이때 이 @class directive를 사용하면 됩니다!
Interface 용도
Interface 파일은 다음과 같은 용도로 설명할수 있습니다.
1. 사용자들이 클래스의 상소관꼐를 파악 할 수 있도록 합니다.
2. 컴파일러에게 클래스가 어떤 인스턴스 변수를 가지고 있는지.. 어떤 변수를 상속할지 등을 알려줍니다.
3. 메소드를 선언함으로써 메시지를 처리할때 리시버가 그 메시지를 처리할수 있는지 파악할수 있게 해줍니다.
Implementation 용도
클래스의 구현은 그 선언과 무척 비슷하게 합니다.
@implementation 클래스이름 : 슈퍼클래스 {
인스턴스 변수등 . . .
}
메소드 구현
@end
모든 Implementation 파일은 그에 대응하는 interface 파일을 import 해야 합니다.
이렇게 함으로써 implementation은 다음것들을 생략 할 수 있습니다.
1. 슈퍼 클래스의 이름
2. 인스턴스 변수들의 선언
이렇게 interface 파일을 import 하면 위의 선언을 다음과 같이 줄일 수 있습니다.
@import "ClassName.h"
@implementation ClassName
메소드 구현
@end
클래스 메소드는 앞에서 언급했듯이 + 사인을 앞에 붙입니다.
+alloc{
....
}
-(BOOL) isFin{
...
}
-(void) SetVal : (int) val{
...
}
또한 클래스 객체의 초기화는 앞에서 설명했듯이 initiallize 라는 클래스 메소드를 사용합니다.
+(void)initiallize{
...
}
갯수가 정해지지 않은 인자를 갖는 메소드는 다음과 같이 정의 합니다.
- getGroup : group , ... , ...... {
...
}
오브젝티브에서 특이한 점은 인스턴스 변수 이름과 같은 메소드이름을 사용할 수 있다는 점입니다.
인스턴스 변수 엑서스
클래스의 인스턴스 변수는 그 클래스의 인스턴스 메소드에서 자유롭게 엑서스 할 수 있습니다.
컴파일러가 내부적으로 클래스의 구조에 대한 C struct를 만들지만 프로그래머는 그것을 쓸 필요가 없죠.
그래서 . 이나 -> 연산자 같은 suruct operator을 사용하지 않아도 됩니다.
예를 들어 다음의 메소드는 리시버의 filled 인스턴스 변수를 사용합니다.
-(void) setFilled:(BOOL) flag{
filled = flag;
}
리시버가 아닌 객체에 속하는 인스턴스 변수를 쓸때는 객체의 타입을 static typing을 통해서 컴파일러에게 알려줘야 합니다.
이때 -> 같은 오퍼레이터를 사용할 수 있습니다.
예를 들면
@interface SSS : NSObject{
SSS * twin;
int val;
struct features *appearance;
}
static 하게 코딩된 객체의 인스턴스 변수가 클래스의 scope(범위)내에 있는한 SSS 메소드는 그 변수를 직접 세팅할수 있습니다.
-makeIdentification{
if ( ! twin ){
twin = [ [ SSS alloc ] init ];
twin->val = val;
twin->appearance;
}
return twin;
}
이건 상당히 특이한 것입니다.
비록. 두 다른 인스턴스 객체가 같은 클래스의 형태라고 하더라도 직접 세팅이 가능합니다.
그렇다고 해도 오브잭티브에 private 같은게 없지는 않습니다.
인스턴스 변수의 범위
c++과 마찬가지로 3가지 권한을 지정할수 있습니다.
@private // 선언한 클래스 내에서만 사용가능하고 상속은 불가
@protected // 선언한 클래스와 상속한 클래스에서도 사용이 가능.
@public // 완전 공개. 무엇이든 엑서스 가능함.
이것들의 범위는 C++ 과 같습니다.
이것들을 않써준다면 C++과 마찬가지로 기본으로 @protected 입니다.
이런 권한을 이용하여 C++과 마찬가지로 상속을 할 수 있습니다.
하지만 서브 클래스가 그 수퍼클래스의 인스턴스 변수를 엑서스하지 못하게 막아둘 필요가 있습니다.
그 이유는 다음과 같습니다.
1. 서브 클래스가 그 슈퍼클래스의 인스턴스 변수를 엑서스한다면 그 순간 슈퍼크래스와 서브 클래스는 땔래야 땔수 없습니다.
나중에 변수 이름을 바꾸거나 할때 힘들어질수가 있죠.
2. 서브 클래스가 슈퍼클래스의 인스턴스 변수를 바꾸게 되면 예기치 않는 버그에 노출될 수 있습니다.
이런 걱정은 사실 C++에서는 염두하고 있지 않습니다.
오브잭티브에서는 다이나믹한 객채지향적 매커니즘을 가지고있기 때문에 이런것들을 염두해둬야 합니다.
아무튼 인스턴스 변수를 그것을 선언하는 클래스에서만 사용하려면 @private 를 사용하면 됩니다.
이렇게 하면 서브 클래스에서는 수퍼클래스의 메소드만 이용하여 엑서스 할수 있게 됩니다.
물론 어디서든 이용하려면 @public로 선언해야겠지만요.
work * ceo = [ [ work alloc ] init ];
ceo->boss = nil; // boss는 work클래스에서 @Public로 선언된 것입니다.
이때 ceo는 위의 예처럼 static 하게 코딩 되어야 합니다.
-34 페이지-
메세지 작업을 어떻게 하는가?
오브잭티브는 객채지향적 언어입니다.
이 언어 역시 entry point는 main() 함수이므로 어느정도 procedural(절차)적인 면이 있으며 여전이 절차적으로 코딩할수 있습니다.
하지만 Apple이나 GNU(무료 소프트웨워 제단)이나 인터페이스 빌더에 의해서 만들어지는 코드와 그 기반의 개발 프로세서를 보면
이런 절차적인 면이 C++ 의 MFC 자체의 것에 비해 많이 숨겨졌다는것을 알 수 있습니다.
즉, 프로그래밍 모델을 Object들의 존재로 하고 그들 사이의 통신으로 많이 표현 됩니다.
C++에게 오브잭티브가 말하고 있는 메시지가 무엇인지 설명하다면 C++ 클래스의 맴버 함수들(메소드)이다.
즉, 오브잭트들은 사람들이라 칭하고 메세지는 오브잭트들끼리의 의사소통이라고 생각합니다.
[ myClass setName : my_class_name_string ];
여기서 오브잭티브의 중요한 개념 중 하나인 selector 가 나옵니다.
오브잭티브는 다이나믹한 언어이므로 메세지를 받게 될 객체의 타입이 여러개일수도 있습니다.
그중 그 메세지에 해당하는 메소드는 각 객체마다 다 다르게 구현되어 있을수도 있습니다.
오브잭티브의 런타임은 그런것을 구별해 주어야 적절한 메세지들을 전달할 수 있습니다.
비슷하지만 다른 메소드를도 선택할 수 있게 해주는 개념으로 selector가 나오게 됬습니다.
이것은 C++프로그래머의 입장에서 보면 function pointer을 이용해서 그때 그때 상황에 맞는 메소드/펑션을 호출하는것과 비슷합니다.
메소드 셀렉터들은 dispatch table 이라는곳에 저장이 됩니다.
런타임이 적절한 함수를 찾기 위해서 dispatch table을 이용하는데 이때 각 class 의 isa pointer 를 따라서 그 테이블을
traverse(가로지르다. 횡단. 횡단하다등의 뜻)하게 됩니다.
그러므로 클래스를 정의할때 isa pointer를 갖도록 하는것이 중요합니다.
참고로 NSObject나 NSProxy로부터 상속을 받으면 자동으로 이 isa pointer를 가지게 됩니다.
Selector
-> 읽기 귀찮은분은 그냥 함수포인터라고 생각하시면 될것 같습니다.
컴파일된 selector는 SEL이라는 특별한 타입으로 되어 있습니다.
Selector을 강제로 설정하거나 따오려면 다음과 같이 하면 됩니다.
SEL setWidthHeight;
setWidthHeight = @selector(setWidth:height:);
C++ 프로그래머에겐 setWidth:height:라는 함수의 어드레스를 가져와서 setWidthHeight란 함수 포인터에 저장하는 것과 유사하게 보입니다.(?)
클래스 매소드와 인스턴스 매소드는 같은 이름을 가질 수 있고 그때 selector을 사용할수 있습니다.
하지만 두개 사이의 confusion(혼란)은 없습니다.
다이나믹 바인딩이 되는 경우에는 같은 selector 를 가지는 메소드같은 리턴타입과 같은 파라미터를 가져야 합니다.
이러한 selector을 어떻게 쓰는지 봅시다.
[ friend gossipAbout:aNeighbor];
위의 문장은 friend라는 object의 gossipAboust라는 메소드를 call 하는데 파라메터로 aNeighbor을 준것입니다.
이것을 selector로 구현하면 아래와 같습니다.
[ friend performSelector:@selector(gossipAbout:) withObject:aNeighbor ];
즉 friend라는 객체에서 gossipAbout:method 의 selector을 따다가 수행시켰는데 aNeighbor이라는 객체를 가지고 해당 메소드를 수행합니다.
여기서 느껴지는 것은..
1. 런타임시에 호출할 함수를 결정짓는 느낌이 강하다
2. selector. 이것은 꼭 함수 포인터 같다.
Cocoa프레임워크는 이 selector를 무척 많이 사용합니다.
C++ 개발자에게 이 selector은 정말 당황스러운 기능이죠.
그런데 C++로 callback함수들을 만들고 전달해줄때 결국 invoke(불러오다,들먹이다라는 뜻인듯) 될 함수의 이름을
파라메터로 전달해주는 것과 많이 닮았습니다.
함수의 이름은 그 함수의 pointer 로 작용한다는 점을 생각하면 이 selector을 쓰는것과 그런 callback함수를 사용하는 것과 대단히 흡사합니다.
즉, selector 은 함수포인터라고 생각해두시는게 좋을것 같네요 ㅎㅎ
(그다음 줄에 기입되어 있는 부분은 이해가 불가능.. 35페이지)
Messaging Error 처리ㅡ
어떤 객체에서 메소드를 다이나믹하게 호출했다고 처봅시당.
앞에서 살펴본 selector을 이용해서 일종의 함수 overriding 과 overloading을 하는 셈인데 만약 그 메세지를 받는 객체가
해당 함수를 가지지 못한 때의 문제를 이야기해봅시다.
즉 C++로 치자면 클래스에 있지도 않은 메소드를 호출했을때 말이죠.
static typing을 쓰면 객채등을 이용할때 그 타입을 선언하고 사용하면 오브잭티브컴 컴파일러가 컴파일시 에러를 내 주지만
만약 그 타입이 id 와 같은 다이나믹 타입이고 런타임시 어떤 객체로 일을 하게 될지 결정하게 된다면 문제가 될 것입니다.
그 러 나 오브잭티브는 여기에 대한 해결법을 가지고 있습니다. (스티브잡스 : 어메이징! )
if ( [ anObject respondsToSelector : @selector ( setOrigin:: ) ] )
[ anObject setOrigin:0.0 :0.0 ];
else
fprintf(stderr , "%s에 그런거 메소드 없다\n" , [NSStringFromClass( anObject class] ) cString] );
respondsToSelector는 setOrigin이라는 메세지를 anObject가 알아먹었는지 런타임시 알아내는 함수입니다.
위 소스를 보자면 그것에 반응을 한다면 즉시 사용하지만 없다면 anObject의 클래스가 어떤것인이 그 클래스이름을 출력해준다.
한가지 더 생각해 본다면 주어진 object가 해당 메세지를 처리할 수 없다고 가정해볼게요.
그럼 어떻게 해야 할까요. 경우에 따라서 "즐" 하고 에러 메세지내고 빠져 나갈 수도 있고...
그 능력을 가진놈에게 패스 해줄수도 있습니다.
이 개념이 실제로 오브잭티브에 존재하는데 그것을 "forwarding"이라고 합니다.
Hidden Argument
오브잭티브 클래스의 메소드에는 두가지 숨겨진 인자가 있습니다.
바로 self 와 _cmd 입니다.
self는 C++의 THIS와 마찬가지로 자기 자신의 객체를 지칭합니다.
_cmd는 그 호출된 메소드 이름을 가지고 있죠.
더 정확하게 말하면 해당 매소드가 호출될때 사용된 selector을 이 _cmd가 가지고 있게 되는데 결국 그 selector가 선택한 함수는
그 호출된 메소드 입니다.
그러므로 _cmd는 호출된 메소드의 selector을 가지고 있죠.
이에 대한 소스코드를 한번 보죠.
-strange {
id target = getTheReceiver();
SEL method = getTheMethod();
if ( target == self || method == _cmd )
return nil;
return [target performSelector:method];
}
단 여기서 주의할 점은 self는 C++의 THIS처럼 static스럽지 않다는겁니다.
실제로 message를 받은 객체의 포인터죠.(결국 비슷하다네요 ㄷㄷ)
팁 : xCode의 디버서는 이 self 와 _cmd를 보여줍니당
Message to self and super
오브잭티브에서 self는 C++과 비슷하고 super 역시 비슷합니다. (super 은 뭐지;)
그러므로 새로울것은 없지만 염두해둬야 할것이 있습니다.
C++에서 혼돈할 여지가 없는한 그냥 member functin 이나 그 객체의 variable를 쓸 수 있습니다.
그 앞에 this-> 를 붙여도 상관 없지만 대개 this 는 컴파일러가 혼돈을 일으킬 소지가 있을때 사용하죠.
하지만 오브잭티브에서 메세지 invocation(아.. 이거 뜻이 대채 뭔지; .. 기도..? 주문?)
[ theObject theMethod ]
의 형식을 가지기 때문에 반드시 self를 쓰게 됩니다.
super또한 그 용례에 있어 차이점이 있습니다.
반응형
'모바일개발(Mobile Dev) > IOS개발(ObjectC)' 카테고리의 다른 글
The official raywenderlich.com Objective-C style guide. (0) | 2015.10.02 |
---|---|
xcode @property self variable null (0) | 2015.10.02 |
Encapsulating Data (0) | 2015.10.02 |
How to deal with a Data Flow between Controller1 and Controller2 (0) | 2015.10.01 |
xcode 에서 private, project 형태 (0) | 2015.09.30 |