본문 바로가기
  • 그냥 하자
iOS

Chap7. selector, category, protocol, instance, 매개변수, 프로토콜, 인스턴스, 카테고리, 셀렉터

by Mash 2012. 4. 2.
반응형

<출처 : http://wwwww.kr/?mid=objectivec&page=2&document_srl=31076 >


selector
- 프로그램 내부에서 메서드를 호출하면 컴파일 시 내부에 표현되는 값으로 변경을 가해서 호출합니다.
- 메서드의 내부 표현에 해당하는 데이터 타입을 SEL 타입이라고 합니다.
- SEL타입에 데이터를 대입하는 경우에는 @ selector(메서드명)으로 메서드명을 넘겨주면 됩니다.
 SEL 변수명 = @selector(메서드명);
- 매개 변수가 있는 경우에는 메서드 명 뒤에 :을 붙여서 사용합니다
- @selector를 호출할 때는 객체가 performSelector:@selector(메서드)의 형태나 SEL 변수를 이용합니다.
- 매개변수는 객체만 가능하며 다음과 같이 호출합니다.
 performSelector:@selector(메서드:) withObject:객체
- 이를 적절히 이용하면 동적으로 메시지를 변경할 수 있습니다.
- (SEL)변수 ------> @selector(메서드명)
- 없는 메서드를 호출하거나 매개변수를 안만든 경우 unrecognized selector 오류 발생

 

01.#import <Foundation/Foundation.h>
02.@interface Test : NSObject
03.-(void)Disp;
04.@end
05.@implementation Test
06.-(void)Disp
07.{
08.NSLog(@"Disp 입니다");
09.}
10.@end
11.int main()
12.{
13.Test *Obj = [Test new];
14.[Obj performSelector:@selector(Disp)];
15.[Obj Disp];   
16.return 0;
17.}

결과
2010-11-15 09:37:29.307 Test[409:a0f] Disp 입니다
2010-11-15 09:37:29.309 Test[409:a0f] Disp 입니다

 

01.#import <Foundation/Foundation.h>
02.@interface Test : NSObject
03.-(void)Disp;
04.-(void)Print;
05.@end
06.@implementation Test
07.-(void)Disp
08.{ NSLog(@"Disp 입니다");}
09.-(void)Print
10.{ NSLog(@"Print 입니다");}
11.@end
12.int main()
13.{
14.Test *Obj = [Test new];
15.int sel;
16.NSLog(@"숫자를 입력하세요 1:Disp 2:Print");
17.scanf("%d", &sel);
18.SEL dele;
19.if(sel == 1)
20.dele = @selector(Disp);
21.else
22.dele = @selector(Print);
23.[Obj performSelector:dele];   
24.return 0;
25.}

결과
2010-11-15 09:39:35.597 Test[465:a0f] 숫자를 입력하세요 1:Disp 2:Print
1
2010-11-15 09:39:28.732 Test[454:a0f] Disp 입니다
2
2010-11-15 09:39:36.940 Test[465:a0f] Print 입니다

 

매개 변수가 있는 selector
- 매개 변수가 있는 경우에는 @selector(메서드:) 형태로 정의하고 호출할 때는 
performSelector:@selector(메서드:) withObject:object 
형태로 호출합니다.
- 2개의 매개변수를 사용하면 performSelector:@selector(메서드:) withObject:object  withObject: object 형태 로 호출합니다.

 

01.#import <Foundation/Foundation.h>
02.@interface Test : NSObject
03.{
04.int n;
05.}
06.-(id)init:(int) a;
07.-(void)Disp:(Test *)Temp; //프로토타입이 완전히 똑같아야 함
08.-(void)Print:(Test *)Temp;
09.@property int n;
10.@end
11.@implementation Test
12.@synthesize n;
13.-(id)init:(int) a
14.{
15.self = [super init];
16.if(self != nil)
17.n = a;
18.return self;
19.}
20.-(void)Disp:(Test *)Temp
21.{
22.NSLog(@"%d", ++(Temp.n));
23.}
24.-(void)Print:(Test *)Temp
25.{
26.NSLog(@"%d", --(Temp.n));
27.}
28.@end
29.int main()
30.{
31.Test *Obj = [[Test alloc]init:5];
32.int sel;
33.NSLog(@"숫자를 입력하세요 1:Disp 2:Print");
34.scanf("%d", &sel);
35.SEL dele;
36.if(sel == 1)
37.dele = @selector(Disp:);
38.else
39.dele = @selector(Print:);
40.[Obj performSelector:dele withObject:Obj];   
41.return 0;
42.}

결과
2010-11-15 09:43:29.039 Test[539:a0f] 숫자를 입력하세요 1:Disp 2:Print

 

카테고리
- 클래스의 일부 메서드를 구현한 모듈을 Category라고 합니다.
- 사용자 정의 클래스나 Framework가 제공하는 클래스의 기능을 확장하고자 하는 경우 사용하는 기능입니다.
- 기존 클래스의 정의를 변경하지 않은 상태에서 추가하고자 하는 메서드만 추가해서 사용할 수 있습니다.
- 이를 적절히 이용하면 클래스의 분할 구현도 가능합니다.

 

cf.
NSNumber : 클래스 클러스터
NSNumber *num1 = [NSNumber numberWithInt:100]; //정수
NSNumber *num2 = [NSNumber numberWithFloat:100]; //실수

 

 

카테고리의 생성
클래스의 선언과 정의와 유사하며 단 기존 클래스 이름을 사용해야 하므로 기존 클래스의 헤더파일이 import 되어야 합니다.

1.@interface 클래스명(카테고리명)
2.메서드의 선언;
3.@end
4. 
5.카테고리의 구현
6.@implementation 클래스명(카테고리명)
7.메서드의 정의;
8.@end

 

카테고리 규칙
- 카테고리 이름에는 제한이 없지만 카테고리가 속한 클래스 안에서 카테고리 명과 같은 이름을 사용하는 것은 안됩니다.
- 인스턴스 변수의 선언은 금지됩니다. => 이 경우는 상속을 받아서 처리
- 메서드 선언에는 제한이 없습니다.
- 오버라이딩 하는 것은 바람직하지 않습니다. => 기존 메서드를 호출할 수 없습니다.
- 지역 메서드나 C언어의 함수를 사용하는 것이 모두 가능합니다.
- 파일 명은 일반적으로 클래스명 + 카테고리 명으로 하는 것이 일반적입니다.

 


프레임워크 클래스의 확장
- 카테고리를 사용하는 것은 사용자 정의 클래스에만 해당되는 것은 아닙니다.
- Framework가 제공해주는 클래스에 대해서도 가능합니다.- 
- NSString에는 단위를 붙여서 리턴해주는 메서드가 제공되지 않습니다.
- 사용자는 매번 메서드를 정의해서 사용해야 합니다.
- 이러한 경우 사용자가 이러한 메서드를 확장해서 사용할 수 있으면 매우 편리할 것입니다.
- 이 때 카테고리를 이용하면 매우 유용하게 사용될 것입니다.
- 기존 메서드를 재정의하게 되면 이전 메서드는 가려집니다.


예제

01.//Unit.h(c header file 추가) - add에서 c/c++파일 메뉴에서 헤더파일 추가
02.#import <Foundation/NSString.h>
03.@interface NSString(Unit)
04.- (NSString *)stringWithAmerica;
05.- (NSString *)stringWithKorea;
06.@end
07.@implementation NSString(Unit)
08.//문자열의 앞에 $를 붙여서 리턴하는 메서드
09.- (NSString *)stringWithAmerica;
10.{
11.return [NSString stringWithFormat:@"$%@", self];
12.}
13.//문자열의 앞에 \를 붙여서 리턴하는 메서드
14.- (NSString *)stringWithKorea; 
15.{
16.return [NSString stringWithFormat:@"\\%@", self];
17.
18.@end
01.Main. m파일
02.#import "Unit.h"
03.int main(int argc, char *argv[])
04.{
05.NSAutoreleasePool *pool = [NSAutoreleasePool new];
06.NSString *string = @"1000";     
07.NSLog(@"America:%@", [string stringWithAmerica]);
08.NSLog(@"Korea:%@", [string stringWithKorea]);
09.[pool drain];
10.return 0;
11.}

결과
2010-11-15 10:00:25.111 Test[715:a0f] America:$1000
2010-11-15 10:00:25.116 Test[715:a0f] Korea:\1000

 


Protocol
- Object의 역할이나 동작을 표현하는 메서드의 집합
- Java의 Interface가 Objective-C의 프로토콜을 도입한 개념입니다.
- 여러 개의 클래스 또는 오브젝트가 공통적인 동작을 가지고 있을 때 이를 프로토콜에 정의해 두고 이를 상속받아서 자신의 특성에 맞게 구현 한 후 사용합니다.
- 이렇게 프로토콜로부터 상속받아서 이를 전부 구현 하게 된 경우를 프로토콜을 Conform(준수) 했다고 합니다.
- 프로토콜의 선언

1.@protocol 프로토콜이름
2.메서드 선언;
3.@end

- 일반적으로 프로토콜의 이름은 첫 글자가 대문자로 시작합니다.
위처럼 선언한 후 .h 파일에 저장하는 것이 일반적입니다.


프로토콜의 적용
- 프로토콜을 적용할 때는 아래와 같은 방법을 이용하게 됩니다.

1.@interface 클래스이름: 슈퍼클래스이름 <프로토콜이름>
2.{ 변수 선언; }
3.메서드 선언
4.@end

- 위처럼 작성하면 프로토콜에 선언된 모든 메서드가 선언된 것과 같은 효과를 나타내게 됩니다.
이 때 주의할 점은 프로토콜에 선언된 모든 메서드를 구현해야 하며 여러 개의 프로토콜을 적용하고자 하는 경우 < > 안에서 ,로 구분하여 나열하면 됩니다.
- 프로토콜에 선언된 모든 메서드를 구현하지 않으면 Warning을 나타내게 될 것입니다.
- 카테고리의 선언에도 사용할 수 있으며 이 경우에는 카테고리 이름 다음에 프로토콜을 적용하는 문장을 기재하면 됩니다.


 예제

01.#import <Foundation/NSString.h>
02.//프로토콜의 생성
03.@protocol ProtocolToString
04.- (id) ToString;
05.@end
06.@interface Test : NSObject <ProtocolToString>
07.{
08.NSString *name;
09.}
10.- (id) init;
11.@end
12.@implementation Test
13.- (id) init
14.{
15.name = [NSString stringWithFormat:@"파일명:%s 클래스명:%@" ,__FILE__,self];
16.return self;
17.}
18.- (id) ToString { return (id) name; }
19.@end
20. 
21.int main()
22.{
23.NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];
24.id obj = [Test new];
25.NSLog(@"obj = %@\n" , [obj ToString]);
26.[pool drain];
27.return 0;
28.}

 

01.#import <Foundation/NSString.h>
02.@protocol ProtocolToString
03.- (id) ToString;
04.@end
05.@protocol ProtocolToAddress
06.- (id) ToAddress;
07.@end
08.@interface Test : NSObject <ProtocolToString,ProtocolToAddress >
09.{
10.NSString *name;
11.NSString *address;
12.}
13.- (id) init;
14.@end
15.@implementation Test
16.- (id) init
17.{
18.name = [NSString stringWithFormat:@"파일명:%s 클래스명:%@" ,__FILE__,self];
19.address = [NSString stringWithFormat:@"주소:%d 번지" ,self];
20.return self;
21.}
22.- (id) ToString { return (id)name; }
23.- (id) ToAddress { return (id)address; }
24.@end
25.int main()
26.{
27.NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];
28.id obj = [Test new];
29.NSLog(@"obj = %@\n" , [obj ToString]);
30.NSLog(@"obj의 주소 = %@\n" , [obj ToAddress]);
31.[pool drain];
32.return 0;
33.}

 

인스턴스의 제약 _매개 변수의 제약
인스턴스 변수를 선언할 때 Objective-C는 id 타입으로 선언하게 되면 아무런 제약없이 모든 클래스의 인스턴스를 생성할 수 있습니다.
이런 경우 생성되는 인스턴스에 제약을 가하고자 하는 경우에도 프로토콜을 사용해서 제약을 가할 수 있습니다.
이 때는 동적으로 타입을 확인하지 않고 정적으로 타입을 점검하게 됩니다.


인스턴스 변수의 제약
id <프로토콜이름> 인스턴스변수명;
매개 변수의 제약
-(결과형)메서드명:(id <프로토콜이름>)매개변수명;

 

01.#import <Foundation/NSString.h>
02.@protocol ProtocolToString
03.- (id) ToString;
04.@end
05.@interface Test : NSObject <ProtocolToString>
06.{  NSString *name; }
07.- (id) init;
08.@end
09.@implementation Test
10.- (id) init
11.{
12.name = [NSString stringWithFormat:@"파일명:%s 클래스명:%@" ,__FILE__,self];
13.return self;
14.}
15.- (id) ToString { return (id)name; }
16.@end
17.int main()
18.{
19.NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];
20.Test <ProtocolToString> * obj = [Test new];
21.NSLog(@"obj = %@\n" , [obj ToString]);
22.[pool drain];
23.return 0;
24.}

 

프로토콜의 선택과 필수의 구분
프로토콜에 선언되는 메서드 앞에 @optional 을 기재하면 메서드를 구현해도 되고 하지 않아도 됩니다.
@required를 기재하면 반드시 반드시 구현해야 합니다.

01.#import <Foundation/NSString.h>
02.@protocol ProtocolToString
03.- (id) ToString;
04.@optional
05.- (id) ToAddress;
06.@end
07.@interface Test : NSObject <ProtocolToString>
08.{
09.NSString *name;
10.}
11.- (id) init;
12.@end
13.@implementation Test
14.- (id) init
15.{
16.name = [NSString stringWithFormat:@"파일명:%s 클래스명:%@" ,__FILE__,self];
17.return self;
18.}
19.- (id) ToString { return (id)name; }
20.@end
21.int main() {
22.NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];
23.id obj = [Test new];
24.NSLog(@"obj = %@\n" , [obj ToString]);
25.[pool drain];
26.return 0;
27.}


반응형