'Objective-C(C'에 해당되는 글 1건

  1. 2010.06.22 iPhone 과 Objective-C(C 언어 개발자가 궁금 했던 것들)
오브젝트C2010. 6. 22. 09:48
iPhone 과 Objective-C(C 언어 개발자가 궁금 했던 것들)


objective-c 공부하면서 궁금하고 생소한 내용

@synthesize

@selector

@protocol



다음 싸이트 번역본중 일부 입니다..
http://wisdom.sakura.ne.jp/programming/objc/index.html
그리고 다음싸이트도 참고 합니다.
http://theeye.pe.kr/



클래스 메서드 & 인스턴스 메서드


Object 클래스는 Object 클래스를 포함하여 그것을 상속하는 클래스의 인스턴스를 제대로 생성 alloc 클래스 메서드를 정의합니다. 일반적인 인스턴스 메서드는 메서드를 호출하는 인스턴스가 필요하지만 클래스 메서드는 인스턴스없이도 실행될 수있다는 성질이있습니다. 따라서 alloc 메소드는 인스턴스가 존재하지 않더라도 호출에 문제가없습니다. alloc 메서드는 인스턴스를 생성하기위한 클래스 메서드이므로 자주 팩토리 메소드라고도합니다



Self

암시적 self

메서드 범위 내에서에는 임시 인수 및 선언된 변수 이외에 절대적인 변수로 self를 정의합니다.self 변수는 id 형식 항상 메서드를 호출 인스턴스를 참조합니다.즉, 메서드를 실행하는 개체 자신을 나타내는 변수가 self입니다.

self는 메소드 이외의 장소에서는 사용하지 않고 항상 메서드 않습니다 실행 코드 부분에서만 암시 값이 존재합니다.self를 사용하여 개체의 인스턴스 변수에 개체에서 볼 수있습니다.이것은 메서드의 정식 인수 변수 이름 인스턴스 변수 이름을 은폐했다 때 등에 사용할 수있습니다.




Property와 Synthesizie


Property와 Synthesizie를 이야기 하면서 왜 갑자기 MVC이야기가 나왔을까요?

이들 사이에 밀접한 연관이 있기 ‹š문입니다. 실제로 Property&Synthesize는 MVC의 Model에 해당합니다.

Property는 어플리케이션에 필요한 데이터를 신뢰할 수 있고 안전하게 보존하는데 필요한 룰을 제공합니다.

Synthesize는 Property에서 선언한 데이터들을 외부에서 합법적으로 접근할 수 있는 통로를 편리하게 제공하여 줍니다.

우선 다음을 예제 코드를 보도록 하겠습니다. 아래에 대한 의문이 있으시다면 [이글]을 먼저 읽어주세요.

// Model.h
#import <Cocoa/Cocoa.h>

@interfaceModel:NSObject{
   
NSString*data;
}
@property(copy, readwrite)NSString*data;
@end

// Model.m
#import "Model.h"

@implementationModel
@synthesize data;
@end

위와 같이 작성한 코드는 이제 다른 객체에서 다음과 같이 사용할 수 있습니다.
#import <Foundation/Foundation.h>
#import "Model.h"

int main(int argc,constchar* argv[]){
   
NSAutoreleasePool* pool =[[NSAutoreleasePool alloc] init];

   
Model* model =[[Model alloc] init];

   
// synthesize를 통해 생성된 setter, getter사용법
   
[model setData:@"Hello Eye!"];
   
NSLog([model data]);// Hello Eye!
   
   
// dot 문법 사용하기
    model
.data =@"Bye Eye!";
   
NSLog(model.data);// Bye Eye!

   
[pool drain];
   
return0;
}

위의 예제에서 알 수 있듯이 model객체의 data 인스턴스 변수에 접근하기 위해서는 synthesize를 통해 생성된 getter(data)와 setter(setData)를 통해 가능합니다.

혹은 C에서 익숙한 객체와 변수사이에 점을 찍는 방식도 사용가능합니다.

하지만 여기서 중요한점은 Property입니다. Model.h의 property선언 부분을 다음과 같이 고쳐볼까요.
@property(copy,readonly)NSString*data;

바뀐점은 readwrite에서 readonly로 바뀌었습니다. 실행해 보면 에러가 발생하는 것을 알 수 있습니다.

error: object cannot be set - either readonly property or no setter found

property와 synthesize를 이용해서 MVC에서 추구하는 데이터를 신뢰할 수 있고 안전하게 사용할 수 있는 방법을 손쉽게 구현할 수 있다는 것을 알게 되었습니다.

여기서 괜히 궁금한것이 생겼습니다. synthesize를 통해서 생성된 setter, getter가 해당 변수를 보호하는 것일까요 property를 통해 정의된 변수 그 자체가 보호하는 것일까요?

이 궁금증을 해소하기 위해 setter를 하나만 만들어 보기로 할까요. 코드에 다음을 추가합니다.
// Model.h
// 현재 @property설정에 readonly로 되어있다고 가정
-(void)setCustomData:(NSString*)myData;

// Model.m
-(void)setCustomData:(NSString*)myData {
   
self.data = myData;
}

이제 내가 만든 setCustomData메서드에 메시지를 전달해 보겠습니다.
Model* model =[[Model alloc] init];
[model setCustomData:@"Hello Eye!"];

이 코드를 실행해 보면 아무런 문제가 발생하지 않습니다. 해당 변수가 보호받는것이 아님을 알 수 있습니다.

앞으로는 코드를 작성할 때 @property로 지정한 변수는 될수 있다면 @synthesize를 통해 접근하는 버릇을 들여야 할 것 같습니다.



클래스 형식

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; )


selector



Objective - C 컴파일러는 메서드를 식별하는 이름을 컴파 일할 때 내부 표현으로 변환합니다.이 메서드의 내부 표현을 선택기라고 메시지를 보내고받는 뒷면은이 셀렉터가 교환되고있습니다.메서드를 식별하기위한 내부 표현은 컴파일러에 의존하는 문제이고, 개발자가 알아야하는 범위는없습니다.개발자에게 중요한 것은이 선택기를 SEL 형식으로 취급할 수있다는 사실입니다.

메서드가 어떤 데이터로 변환되고, 어떻게 인식되고있다는 문제가없습니다.하지만 Objective - C은이 내부 표현을 SEL 변수로 취급하는 것을 보장합니다.즉, SEL 변수에는 메서드 이름을 확인하기 위하여 컴파일러가 할당 특수 코드를 저장할 수있다는 것입니다.

메서드를 식별 선택기는 @ selector 컴파일러 지시문을 사용하여 얻을 수있습니다.

@ selector (method)

method는 셀렉터를 원한다 메서드의 이름을 지정합니다.지정된 메서드의 이름이 있는지 여부는 메서드를 호출하는 경우 런타임에 결정되므로 컴파일 타임에 계산되지 않을 것입니다.

에서 얻은 셀렉터 값을 SEL 변수에 저장했다고해서, 이것을 어떻게 사용할 수있는 것일까요?선택 기가 메서드를 식별하는 성격이있는 이상, 역시 최대의 이용 방법은 함수에 대한 포인터로 동적으로 메서드를 식별하는 방법입니다.선택기에서 메서드를 호출하는 기능을 제공하는 루트 클래스입니다.

Object 클래스는 SEL 형식의 값을받는 perform 메소드가 선언되어있습니다.이 메서드는 인수로받은 선택기에서 특정 메서드를 실행합니다.

- perform : (SEL) aSel;

- perform : (SEL) aSel with : anObject;

- perform : (SEL) aSel with : anObject1 with : anObject2;

aSel를 호출할 메서드를 선택기를 지정합니다.anObject, anObject1, anObject2에는 메서드에 전달할 인수를 지정할 수있습니다.

 # import <stdio.h> # import <objc/Object.h> @ interface Test : Object - (void) Write; @ end @ implementation Test - (void) (Write printf ( "I am the bone of my sword \ n"); ) @ end int main () ( id obj; SEL method; obj = [Test new]; method = @ selector (Write); [obj perform : method]; return 0; )

이 프로그램은 Write 메서드를 나타내는 선택기를 SEL 변수 method에 저장합니다.그리고 Test 클래스의 인스턴스 obj에 perform 메시지 method를 인수로 전달합니다.perform 메소드는 주어진 선택기에서 실행해야하는 방법을 알아 실행합니다. 이 성질을 잘 이용하면 런타임에 호출하는 방법을 상황에 따라 전환과 같은 프로그램을 구현할 수있습니다.



메서드를 함수 호출

사실, Objective - C 방법의 실태는 C 함수와 동일합니다.평소에는 은폐되어 있지만 메서드는 첫 번째 인수에 자신의 

클래스를 참조하는 개체를받는 함수입니다.

메서드의 개체가 함수이다는 사실은 C 언어와 친화성이 매우 높다는 것을 의미합니다.순수한 C로 작성된 라이브러리에서 

Objective - C의 메서드를 호출하거나 개체를 이용하는 것도 무리한 이야기는 아닙니다.

Objective - C 메서드는 항상 IMP 형식으로 정의됩니다.IMP 형식은 헤더 파일에 다음과 같이 정의되어있습니다.


typedef id (* IMP) (id, SEL, ...);

이 정의에서 알 수 있듯이, Objective - C에 선언된 모든 메서드는 암시적으로 id 형식 SEL 형식의 인수를 가집니다.

첫번째 인수는 메서드를 호출하는 개체를 나타내는 변수 self합니다.두 번째 인수는이 메서드의 셀렉터를 나타내는 변수 

_cmd입니다.모든 메서드는이 숨겨진 인수가 항상 존재합니다.그런 다음 메서드 선언에 따라 인수가 결정되는 것입니다.


C 언어, 어떤 이유로 Objectiver - C에서 메서드를 함수로 호출해야하는 경우 메서드를 참조하는 함수에 대한 포인터를 

취득하면 되나요.메서드를 참조하는 IMG 형식의 포인터는 Object 클래스의 instanceMethodFor 클래스 메서드 또는

methodFor 인스턴스 메서드에서 얻을 수있습니다.


+ (IMP) instanceMethodFor : (SEL) aSel;

- (IMP) methodFor : (SEL) aSel;


aSel에는 해당 메서드 IMP, 즉 함수에 대한 포인터를 검색하고 선택기를 지정합니다.메서드는 aSel에 지정된 선택기에서 

특정 메서드의 포인터를 반환합니다.

포인터를 얻을 수있는 경우, C 언어에서도 인스턴스 메서드를 호출할 수있습니다.함수 포인터로 직접 호출, 메시지 전달보다 

신속하게된다는 점도 특징입니다.

 # import <stdio.h> # import <objc/Object.h> @ interface Test : Object - (void) Write; @ end @ implementation Test - (void) (Write printf ( "I am the bone of my sword \ n"); ) @ end int main () ( id obj; SEL method; IMP func; obj = [Test new]; method = @ selector (Write); func = [Test instanceMethodFor : method]; func (obj, method); return 0; )

이 프로그램은 Test 클래스의 인스턴스 메서드 Write에 대한 포인터를 가져, IMP 변수 func에 저장합니다.

[Test instanceMethodFor : method] 메시지있는 부분은, obj methodFor : method]로 의미는 동일합니다.

이 메시지 식을 반환 IMP 형식의 메서드의 포인터를 사용하여 직접 메서드를 호출합니다.


이미지 프로세싱 및 멀티미디어와 같은 루프 속도 향상 등이 필요한 경우에는 서신이나 선택기 통신은 부담이 큽니다.

속도에 집착하는 프로그램이라면, 필요에 따라 IMP 가져 포인터에서 메서드를 호출해도 좋습니다.





클래스에 메서드 추가

Objective - C는 클래스를 여러 파일에 나누어 선언 및 정의하는 기능을 제공합니다.이것을 분류라고 범주를 사용하면 

클래스의 메서드 선언을 나눌 수있습니다.

예를 들면, 여러 개발자가 공동으로 동시에 평행 동일 클래스의 구현을 작성하는 경우 각 개발자는 클래스 중에 개발자가

담당하는 메서드를 카테고리로 분류합니다.

카테고리를 선언하려면 반드시 기본이되는 클래스의 인터페이스 선언이 필요합니다.클래스 종류를 선언 및 정의하려면 

다음 구문을 지정합니다.


@ interfae 클래스 이름 (카테고리 명) (...

@ implementation 클래스 이름 (카테고리 명) (...


여기서 지정한 카테고리 이름은 C 언어의 식별자 명명 규칙을 따릅니다.범주 화하는 클래스는 반드시 이미 기본이되는

본체가 선언되어 있어야합니다.


분류는 일반적으로 클래스 선언과 정의와 비슷하지만, 인스턴스 변수를 선언할 수 없기 때문에주의하십시오.분류 선언할 

수있다는 인스턴스 메서드와 클래스 메서드뿐입니다.

 # import <stdio.h> # import <objc/Object.h> @ interface Test : Object - (void) WriteA; @ end @ interface Test (Fate) - (void) WriteB; @ end @ implementation Test - (void) WriteA ( printf ( "I am the bone of my sword \ n"); ) @ end @ implementation Test (Fate) - (void) WriteB ( printf ( "몸은 검으로되어있는 \ n"); ) @ end int main () ( id obj = [Test new]; [obj WriteA]; [obj WriteB]; [obj free; return 0; )

이 프로그램은 Test 클래스를 주로 Fate이라는 이름의 카테고리로 분류하고있습니다.실행 결과는 상상과 같습니다.

Test 클래스의 연속되는 모든 범주는 궁극적으로 동일한 클래스로 통합합니다.물론 분류는 다른 파일에 선언해도 

상관없습니다.만일 그렇다면, 카테고리를 선언하는 파일은 기본이되는 클래스를 선언하는 헤더 파일을 포함하고 있어야합니다.


분류는 기존의 완성하는 클래스에 기능을 추가하는 용도로 사용할 수도있습니다.그러나 인스턴스 변수를 추가하는 것은 

할 수 없기 때문에 기존 클래스의 역할 범위의 확장이라는 것입니다.

 # import <stdio.h> # import <objc/Object.h> @ interface Object (Write) - (void) Write; @ end @ implementation Object (Write) - (void) (Write printf ( "I am the bone of my sword \ n"); ) @ end int main () ( id obj = [Object new]; [obj Write]; [obj free; return 0; )

이 프로그램은 루 유익 라스이다 Object 클래스에 새 분류를 추가합니다.덧붙여서, 분류 방법이 기존 클래스의 메서드와 

충돌하면 분류 방법이 우선 기존의 방법은 은폐되어 버립니다.상속을 통해 재정과 다르기 때문에, 은폐되는 메서드를 호출하는 

방법이 없을 것이기 때문에주의하십시오.



메서드를 클래스와 공유하기

프로토콜이란 상대적으로 네트워크 용어로 사용되고 있지만, Objective - C에 한해서는 여러 클래스에서 구현되는 동일한 

이름의 메소드를 공유하기위한 메서드 선언을 말합니다 .Java 언어와 C # 언어에서는 인터페이스라는 개념에 가까운

존재입니다.

프로토콜이 사용되는 프로그램의 설계 문제에 클래스가 특정 메서드를 구현하는 것을 보장하기위한 수단으로 사용됩니다.

예를 들어 특정 라이브러리 사양에 따라 클래스는 지정된 프로토콜을 따르도록해야한다,와 같은 관계를 구축할 수있습니다.

프로토콜을 제공하는 선언된 메서드만에서 메서드의 구현이 어떤 작업을 수행할은 자유입니다.

프로토콜을 선언하려면 @ protocol 컴파일러 지시문을 사용합니다.이 가운데 프로토콜이 조건으로 지정하는 메서드를

선언합니다.

 @ protocol 프로토콜 이름 "부모 프로토콜 1, ..." 프로토콜 본체 ... @ end

프로토콜은 클래스의 상속 관계와 부모 프로토콜을 사용하여 상속할 수있습니다.프로토콜의 상속에 대해서는 뒷부분에서 

자세히 설명 하겠지만, 프로토콜 상속은 단순히 부모 프로토콜을 결정하는 방법에 새 메서드를 추가하는 의미 밖에 가지지

않습니다.프로토콜 이름은 클래스 이름과 같은 것처럼 C 언어의 명명 규칙에 따라 전문 언어 리스크를 식별하는 이름을 

지정합니다.프로토콜 본문은 메서드 선언만을 할 수있습니다.


선언했다 프로토콜은 클래스에 채택될 수있습니다.프로토콜을 지정하는 클래스는 해당 프로토콜을 채택하고있다라고 할 

수있습니다.프로토콜을 사용하는 클래스는 해당 프로토콜에 선언된 메서드를 반드시 구현해야합니다.덧붙여서, 프로토콜에 

선언된 메서드를 프로토콜을 사용하는 클래스의 선언 부에서 다시 선언할 필요가없습니다.프로토콜 종류에 사용하기 위해서는

다음과 같이 클래스를 선언합니다.


@ interface 클래스명 : 슈퍼 클래스 이름 "프로토콜 1, ..."


프로토콜은 슈퍼 클래스의 지정된 직후 ""에서 프로토콜 이름을 묶어 지정합니다.슈퍼 1 개만 지정할 수 있지만, 프로토콜은 

콤마,로 구분하여 여러 개의 프로토콜을 사용하도록 할 수있습니다.프로토콜을 사용하는 경우, 반드시 프로토콜에 선언된 

메서드를 @ implementation 부에서 구현해야합니다.즉, ぷろとこるを 채용한다는 것은 그 종류가 프로토콜 선언되는 

메서드의 구현을 보장한다는 것입니다.

 # import <stdio.h> # import <objc/Object.h> @ protocol ClassNameToString - (id) ToString; @ end @ interface A : Object ( char * name; ) - (id) init; - (id) free; @ end @ interface B : Object @ end @ implementation A - (id) init ( [super init]; name = (char *) malloc (255); sprintf (name, "% s A @ % d", __FILE__, self); return self; ) - (id) (free free (name); return [super free; ) - (id) ToString (return (id) name;) @ end @ implementation B - (id) ToString (return (id) "This is Object of B Class";) @ end int main () ( id objA = [A new]; id objB = [B new]; printf ( "objA = % s \ n", objA ToString); printf ( "objB = % s \ n", objB ToString); [objA free; [objB free; return 0; )

이 프로그램은 클래스의 이름을 나타내는 문자열을 반환하는 ToString 메서드를 선언 ClassNameToString 프로토콜을 

선언합니다.이 프로토콜을 사용하는 A 클래스와 B 클래스는 반드시 ToString 메서드를 구현해야합니다.A 클래스와 B 클래스의

상속 관계에 아무런 연계도 없지만 프로토콜을 사용하여 이러한 클래스 ToString 메서드가 구현되어있다는 것을 확실하게 

지킬 수있습니다.

실천은 클래스의 구현에 의존하지 않는 완전한 추상화된 형식으로 프로토콜을 사용하는 외에, 포인터를 사용하지 않고 메서드가

호출하는 방법으로 사용할 수있습니다.특히 GUI 환경 이벤트 처리 등이 프로토콜을 사용하는 것입니다.

프로토콜 형식으로 독립하지 않지만 형식 선언에서 형식 이름이 바로 ""에 프로토콜을 지정하기위한 변수가 지정된 프로토콜을

사용하는 것을 명시적으로 선언 수 있습니다.


모델명 "프로토콜 이름"변수 이름 ...


이것은 함수 및 메서드의 인수 선언도 지정할 수있습니다.

 # import <stdio.h> # import <objc/Object.h> @ protocol InstanceListener - (void) InstanceFree : (id) object; @ end @ interface Test : Object ( id <InstanceListener> listener; ) - (id) init; - (id) free; - (void) SetInstanceListener : (id <InstanceListener>) l; - (id <InstanceListener>) GetInstanceListener; @ end @ implementation Test - (id) init ( [super init]; listener = NULL; return self; ) - (id) (free if (listener) listener InstanceFree : self]; return [super free; ) - (void) SetInstanceListener : (id <InstanceListener>) l ( listener = l; ) - (id <InstanceListener>) GetInstanceListener ( return listener; ) @ end @ interface WriteInstanceFree : Object <InstanceListener> @ end @ implementation WriteInstanceFree - (void) InstanceFree : (id) object ( printf ( "% X : 인스턴스가 해제되었습니다 \ n", object); ) @ end int main () ( id obj1 = [Test new, obj2 = [Test new]; id <InstanceListener> listener = [WriteInstanceFree new]; [obj1 SetInstanceListener : listener; [obj2 SetInstanceListener : listener; [obj1 free; [obj2 free; return 0; )

이 프로그램은 인스턴스가 풀어 놓은 시간에 호출되는 콜백 전문 InstanceFree 메서드를 선언하는 InstanceListener 프로토콜을

선언합니다.Test 클래스는이 프로토콜을 사용하는 인스턴스를 SetInstanceListener 메서드에서 설정할 수 있고 Test 클래스의

인스턴스가 방출되기 직전에 프로토콜 메서드를 호출합니다.GUI 이벤트 처리는 이와 동일한 방식으로 처리되는 것입니다.


변수를 선언할 때, 형명 직후에 프로토콜을 지정하면 해당 개체가 지정된 프로토콜을 채택해야한다는 것을 명시합니다.

따라서 Test 클래스의 listener 인스턴스 변수는 확실한 목표 메서드가 호출 할 수있습니다.


프로토콜 상속

프로토콜은 클래스처럼 상속받을 수있습니다.프로토콜을 상속은 클래스 상속과 성격이 다르고, 실제로는 기본이되는 

프로토콜에 새 메서드 선언을 추가하면됩니다.또한, 슈퍼 클래스는 반드시 1 개만 지정할 수없습니다 프로토콜은 다중 

프로토콜을 상속할 수있습니다.

 # import <stdio.h> # import <objc/Object.h> @ protocol ProtocolA - (void) MethodA; @ end @ protocol ProtocolB - (void) MethodB; @ end @ protocol ProtocolC <ProtocolA> - (void) MethodC; @ end @ interface Test : Object <ProtocolB, ProtocolC> @ end @ implementation Test - (void) MethodA (printf ( "This is MethodA \ n");) - (void) MethodB (printf ( "This is MethodB \ n");) - (void) MethodC (printf ( "This is MethodC \ n");) @ end int main () ( id obj = [Test new]; [obj MethodA]; [obj MethodB]; [obj MethodC]; [obj free; return 0; )

이 프로그램의 ProtocolC 프로토콜 ProtocolA 프로토콜을 상속합니다.따라서 ProtocolC는 MethodA과 MethodC가 선언되어 

있다고 생각합니다.

또한 Test 클래스는 ProtocolB과 ProtocolC을 함께 이용하고있는 점에도 주목하십시오.선언 밖에 보유하지 않은 프로토콜 

메서드 이름이 충돌하면 해당 프로그램의 메서드 검색에는 영향이 없기 때문에 가능한 것입니다.그러나 다른 의도 메서드

가 프로토콜의 상속 관계에서 충돌하는 경우 주의해야 할 것입니다.




출처 : http://blog.naver.com/PostView.nhn?blogId=inetami&logNo=20101809687
Posted by 오늘마감

댓글을 달아 주세요