검색결과 리스트
2011/03/22에 해당되는 글 1건
- 2011.03.22 Protocol 과 Category
글
Protocol 과 Category
Objective-C
2011. 3. 22. 23:53
이번 포스팅에서는 Protocol과 Category에 대해서 알아보자. Protocol은 자바의 인터페이스에 유사한 개념이다.
Protocol은 공식 프로토콜과 비공식 프로토콜이 있으며, 그 중 공식 프로토콜이 자바의 인터페이스에 해당한다.
Category는 상속의 대안으로 클래스를 변경하지 않고도, 새로운 기능(메서드)을 추가할 수 있는 메커니즘이다.
프로토콜과 카테고리에 대해서 좀 더 자세히 알아보자.
Protocol은 공식 프로토콜과 비공식 프로토콜이 있으며, 그 중 공식 프로토콜이 자바의 인터페이스에 해당한다.
Category는 상속의 대안으로 클래스를 변경하지 않고도, 새로운 기능(메서드)을 추가할 수 있는 메커니즘이다.
프로토콜과 카테고리에 대해서 좀 더 자세히 알아보자.
1. Protocol 이란?
자바언어의 Interface에 해당하는 개념으로 @protocol 지시어를 사용하여 정의한다. @protocol 을 구현하는 모듈은
프로토콜이 규약하는 모든 메소드를 반드시 구현해야하는 프로토콜을 공식 프로토콜(Formal Protocol)이라고 하며,
구현해도 되고 구현하지 않아도 되는 프로토콜을 비공식 프로토콜 (Informal Protocol)이라고 한다. 공식 프로토콜은
@required 지시어를, 비공식 프로토콜은 @optional 지시어를 사용한다.
(비공식 프로토콜은 런타임시에 비공식 프로토콜을 구현하는 메소드가 존재하는 경우에만 호출된다.)
프로토콜이 규약하는 모든 메소드를 반드시 구현해야하는 프로토콜을 공식 프로토콜(Formal Protocol)이라고 하며,
구현해도 되고 구현하지 않아도 되는 프로토콜을 비공식 프로토콜 (Informal Protocol)이라고 한다. 공식 프로토콜은
@required 지시어를, 비공식 프로토콜은 @optional 지시어를 사용한다.
(비공식 프로토콜은 런타임시에 비공식 프로토콜을 구현하는 메소드가 존재하는 경우에만 호출된다.)
공식프로토콜의 모든 메소드들이 구현되지 않으면 컴파일 시에 에러가 발생할 것이다. 2개 이상의 프로토콜을 구현할
경우 ,(콤마)를 구분자로 구현할 프로토콜을 <> 안에 나열한다.
경우 ,(콤마)를 구분자로 구현할 프로토콜을 <> 안에 나열한다.
Objective-C 에서는 Java 와는 다르게 컴파일 타임에 프로토콜 할당에 대해 타입검사를 수행하지 않는다.
런타임시 메시지가 호출될 때, 해당하는 메소드가 구현되지 않았다면 selector exception 예외가 발생한다.
@interface VenusAttacks : Game ... @end @class Thing; @protocol Living - (float)age; - (float)health; (NSDictionary*)healthInfo; @end @protocol Communicating - (NSArray*)recipientsInRange; - (void)sendMessage:(NSString*)message to:(id)recipient; @end @interface Thing : NSObject ... @end @interface Character : Thing ... @end
1.1 비공식 프로토콜(Informal Protocol)
비공식 프로토콜은 모듈클래스가 반드시 구현하지 않아도 되는 프로토콜이다.
비공식 프로토콜은 (반드시 구현하지 않아도 되기 때문에) 유연하고 동적이지만, 추가적인 문서화와 프로그래머들 간에
(통일된 사용을 위해) 협조가 요구된다. 비공식 프로토콜에서는 메서드가 구현되었는지 검사하는 코드가 들어가며
그 형태는 아래 코드와 같다. 자바에서는 윈도우 종료시 이벤트를 인터셉트 하기위해서, 해당하는 이벤트에 대한 리스너를
등록하고, 이벤트가 발생하면 리스너를 통해 통보받는다. Objetive-C 에서는 delegate 가 윈도우 종료시 Notification 을
(통일된 사용을 위해) 협조가 요구된다. 비공식 프로토콜에서는 메서드가 구현되었는지 검사하는 코드가 들어가며
그 형태는 아래 코드와 같다. 자바에서는 윈도우 종료시 이벤트를 인터셉트 하기위해서, 해당하는 이벤트에 대한 리스너를
등록하고, 이벤트가 발생하면 리스너를 통해 통보받는다. Objetive-C 에서는 delegate 가 윈도우 종료시 Notification 을
통보받는 메소드를 비공식 프로토콜로 선언하고 있다.
BOOL shouldClose = YES; if ([delegate respondsToSelector:@selector(windowShouldClose:)]) shouldClose = [delegate windowShouldClose:self]; if (!shouldClose) return;
1.2 공식프로토콜과 비공식 프로토콜의 조합(Combining Formal and Informal Protocols)
프로토콜은 비공식 프로토콜과 공식 프로토콜이 혼합되어 사용되어지는 경우가 많다.
프로토콜에 선언한 메소드는 디폴트로 공식 프로토콜이며, @required 이라는 지시어를 명시적으로 사용할 수 있다.
비공식 프로토콜은 @optional 지시어를 사용한다.
다음은 TableDataSource 프로토콜이며, 테이블 행의 개수를 반환하는 메소드와 해당하는 열과 행의 셀객체를 반환하는
메소드는 @required 로 , 테이블 특정 행과 열의 셀객체를 변경하는 메소드는 @optional 로 선언되어 있어서, 비공식 프로토콜이
구현되지 않았을 경우, read-only 로 동작하게 된다. (구현 유무에 따라 동작용도를 변경할 수 있다. )
@protocol TableDataSource
@required
- (int)numberOfRowsInTable:(Table*)table;
- (id)table:(Table*)table objectForColumn:(int)col row:(int)ro
@optional
- (void)table:(Table*) setObject:(id)object forColumn:(int)col row:(int)row;
@end
클래스 정의 일부에 이름을 붙힌 조각이라 할 수 있다.. 자바에서는 단일블럭에서 하나의 클래스 정의가 모두 이루어지지만,
Objective-C에서는 한 클래스 정의를 카테고리를 사용하여 여러 파일에 분리하여 정의할 수 있다.
카테고리를 사용함으로써, 복잡한 클래스를 관련이 되어 있는 인스턴스 변수와 메소드들의 그룹으로 분리하여
복잡성을 줄이고, 프로그래머들이 독립적으로 개발할 수 있도록 해준다.
이외에도 카테고리는 클래스 정의(@interface) 와 독립적으로 클래스 메소드를 추가할 수 있게하며,
클래스 구현의 일부를 은닉할 수 있는 메커니즘을 제공한다. 클래스와 카테고리는 별도의 헤더파일과 모듈파일에
저장될 수 있으며, 해당 클래스를 사용하는 클라이언트 클래스는 관련있는 헤더파일만을 import 하여 사용한다.
// 카테고리를 사용하지 않고 단일 클래스에 정의한 경우
@interface RecipeBoxController : NSObject {
NSMutableArray*recipes;
NSMutableDictionary* recipeIndex;
}
- (id)init;
- (Document*)newRecipe;
- (Document*)newShoppingList;
- (Document*)newShoppingListFromRecipes:(NSIndexSet*)recipeIndexes;
@end
@implementation RecipeBoxController
...
@end
다음 카테고리는 리스트 설정하고 획득하는 역할만을 수행한다.
// Category 를 사용하여 클래스를 분리 @interface RecipeBoxController (ShoppingLists) - (Document*)newShoppingList; - (Document*)newShoppingListFromRecipes:(NSIndexSet*)recipeIndexes; @end @implementation RecipeBoxController (ShoppingLists) ... @end
2.1 카테고리 사용하여 조직하기(Using Categories for Organization)
거대한 클래스를 다룰수 있는 기능적인 단위로 나누는 것이 카테고리의 주요한 용도이다. 이 개념은 한 클래스를
모듈화 할 수 있게하며, 각 모듈간에 캡슐화와 의존성을 줄여준다. 빌터패턴이 카테고리를 사용하여 얻을 수 있는 응용의 한
예이다. 자바에서 빌드에서 복잡한 객체 생성의 과정이 별도 클래스로 분리되는데,
예이다. 자바에서 빌드에서 복잡한 객체 생성의 과정이 별도 클래스로 분리되는데,
Objective-C에서는 별도의 객체생성과정이 메인 클래스 정으로부터 카테고리를 사용하여 분리될 수 있다.
2.2 메서드 은닉 (Hiding Methods)
카테고리의 또 다른 사용용도는 클래스의 일부를 은닉하는 것이다. 자바에서는 private, protect 키워드를 사용하여
내부 데이터를 은닉한다. 아래는 카테고리를 사용하여 메소드 일부를 은닉하는 예이다. Private 이름으로 선언된 카테고리는
컴파일러에게 무시되며, 해당 클래스를 사용하는 클라이언트 클래스는 카테고리의 메소드들을 볼 수 없다.
내부 데이터를 은닉한다. 아래는 카테고리를 사용하여 메소드 일부를 은닉하는 예이다. Private 이름으로 선언된 카테고리는
컴파일러에게 무시되며, 해당 클래스를 사용하는 클라이언트 클래스는 카테고리의 메소드들을 볼 수 없다.
두개의 클래스 선언을 별도의 헤더 파일로 분리할 수도 있다. 이때, 클라이언트 클래스는 ToasterController.h 헤더파일만을
import 하여 사용하면 된다. 카테고리의 헤더파일은 관례적으로 (클래스이름+카테고리이름.h) 이라는 형태를 갖는다.
아래에서는 ToasterController+Private.h 라는 이름을 갖을 것이다.
이런한 개념을 확장하여 Objectiv-C 에서는 extension 이라는 개념을 제공한다.
@interface ToasterController : NSObject {
@private
float darkness;
}
- (void)setDarkness:(float)level;
- (void)startToasting
- (void)stopToasting
@end
@interface ToasterController (Private)
- (float)darkness
- (CarrierState)carrierPosition;
- (NSTimeInterval)cycleTime;
- (void)setCycleTime:(NSTimeInterval)cycleTime;
- (void)finishedToasting:(NSTimer*)timer;
카테고리를 사용하여 원 클래스에 어떠한 변경을 하지 않고도, 새로운 메소드를 정의할 수 있다.
아래 코드는 Foundation Framework 의 NSArray 클래스에 카테고리를 사용하여, 배열의 첫번째 요소를 얻는
(id)firstObject 메소드를 추가하고 있다.
* 만일 카테고리와 클래스가 동일한 메소드를 구현한다면, 카테고리 메소드가 클래스의 메소드를 대체하게 되어, 본 클래스의
메소드를 호출하지 못하게 된다. 동일한 메소드를 구현하는 카테고리가 2개 이상 있게되면 런타임에 무엇이 호출될지는 예측할 수 없다.
@interface NSArray (MyCollectionAdditions)
- (id)firstObject;
@end
@implementation NSArray (MyCollectionAdditions)
- (id)firstObject {
return ([self objectAtIndex:0]);
}
@end
2.3 Extesions
Extension 은 익명 카테고리이다. 다만 Extension은 클래스의 @implementaion 구현부아 아닌 클래스의 @interface 정의부를
분리하기 위해 사용한다. extension 의 @interface 지시어는 비어있는 카테고리 이름을 사용한다. extensions 내에 선언된 메소드들은 동일하게 @implentation 에서 모두 구현되어져야 한다. 카테고리와 extension은 컴파일 타임에만 프로그래머와 컴파일러로부터 메소드 선언을 은닉할 수 있으며, 런타임시에는 introspection(리플렉션)을 사용하면 노출될 수 있다.
분리하기 위해 사용한다. extension 의 @interface 지시어는 비어있는 카테고리 이름을 사용한다. extensions 내에 선언된 메소드들은 동일하게 @implentation 에서 모두 구현되어져야 한다. 카테고리와 extension은 컴파일 타임에만 프로그래머와 컴파일러로부터 메소드 선언을 은닉할 수 있으며, 런타임시에는 introspection(리플렉션)을 사용하면 노출될 수 있다.
@interface CaseDocument : NSObject {
@private
CaseNumber caseNumber;
}
- (CaseNumber)caseNumber;
@end
@interface CaseDocument()
- (void)setCaseNumber:(CaseNumber)number;
@end
@implementation CaseDocument
- (CaseNumber)caseNumber { return (caseNumber); }
- (void)setCaseNumber:(CaseNumber)number { caseNumber = number; }
'Objective-C' 카테고리의 다른 글
| Objective-C 문자열 조작 메서드 (0) | 2011.04.29 |
|---|---|
| 다중인자를 갖는 PerformSelector 메시지 (0) | 2011.03.23 |
| Protocol 과 Category (0) | 2011.03.22 |
| Objective-C 훑어보기 (2) | 2011.03.20 |
| [Cocoa] Cocoa Design Pattern (0) | 2011.03.20 |
| HMAC-SHA1 구현 (0) | 2011.03.03 |
Category,
extension,
formal protocol,
informal protocol,
objective-c,
protocol,
공식 프로토콜,
비공식 프로토콜,
카테고리,
프로토콜