Objective-C 2.0 에서 부터는 프로그래머의 편의를 위한 기능이 추가된 것이 있다. enum 을 보다 편리하게 사용할 수 있도록 해주는 기능이라고 보면된다. 이것을 Fast Enumeration 이라고 부른다.
예를 들면, 다음과 같다.
NSArray *array = [NSArray arrayWithObjects: @"one", @"two", @"three", nil ];
for( NSString *emt in array ){
NSLog(@"element: %@", emt );
}
for( NSString *emt in array ){
NSLog(@"element: %@", emt );
}
여기서는 NSArray 를 사용하고 있지만, NSDictionary 를 사용하는 것도 상당히 자주 사용되는 방법이다. 또한,Fast Enumeration 을 위해서 NSEnumerator 객체를 사용할 수도 있다. (각 객체에 대한 사항은 레퍼런스문서를 참조하라)
NSEnumeration 객체는 참조 순서를 변경하기 위해 주로 사용된다. 다음의 코드를 살펴보면 NSEnumeartion 객체를 사용하는 경우를 상상할 수 있다.
NSArray *array = [NSArray arrayWithObjects: @"One", @"Two", @"Three", @"Four", nil];
NSEnumerator *enumerator = [array reverseObjectEnumerator];
for( NSString *element in enumerator) {
if( [element isEqualToString:@"Three"] )
break;
}
NSString *next = [enumerator nextObject];
NSEnumerator *enumerator = [array reverseObjectEnumerator];
for( NSString *element in enumerator) {
if( [element isEqualToString:@"Three"] )
break;
}
NSString *next = [enumerator nextObject];
보통 가독성 높은 프로그램 코드를 작성하기 위해 C 프로그래머들도 enum 을 많이 사용하는 것을 볼 수 있는데,Objective-C 를 사용한다면 Fast Enumeration 을 사용해서 손쉽게 보기 좋은 코드를 작성할 수 있을 것이다.
Fast enumeration은 Objective-C 2.0에 새로 추가된 기능 중 하나인데 컬렉션의 모든 맴버를 접근하는 쉽고 간단한 방법이며 NSEnumeration보다 빠르게 작동할 뿐 아니라 코드도 더 읽기 쉽다.
아래와 같이 간단하게 사용할 수 있다.
- for (NSString *string in array)
- {
- NSLog(@"string is %@", string);
- }
NSFastEnumeration 프로토콜을 지원하는 모든 컬렉션(NSArray, NSDictionary, NSSet, NSEnumerator)에서 사용가능하다.
만일 직접 만든 컬렉션에서 fast enumeration을 지원할려고 하면 NSFastEnumeration 프로토콜을 지원하면 된다.
자, 그럼 커스텀 컬렉션에서 fast enumeration을 지원하는 방법을 알아보자.
MyCollection이라는 새로운 컬렉션을 하나 만들고 메인 코드를 다음과 같이 작성하고 빌드를 했다.
- int main (int argc, const char * argv[])
- {
- NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
- MyCollection *collection = [[MyCollection alloc] init];
- for (NSString *string in collection)
- {
- NSLog(@"string = %@", string);
- }
- [collection release];
- [pool drain];
- return 0;
- }
/Users/cgkim/FastEnumTest/FastEnumTest.m:11: warning: 'MyCollection' may not respond to '-countByEnumeratingWithState:objects:count:'
자 다음 코드를 보자. 프로토콜을 지원하기 위해 MyCollection안에 새로 구현한 메소드이다.
- - (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)aState objects:(id *)aStackbuf count:(NSUInteger)aLen
- {
- NSUInteger result = 0;
- if (aState->state < mCount)
- {
- aState->state = mCount;
- aState->itemsPtr = mData;
- aState->mutationsPtr = (unsigned long *)self;
- result = mCount;
- }
- return result;
- }
먼저 NSFastEnumerationState라는 구조체가 있고 id *인 aStackbuf와 NSUInteger인 aLen이 있다.일단 뒤에 두 파라메터는 사용하지 않겠다.
aState->state안에는 현재 어디까지 enumeration했는지에 대한 정보가 들어있다. 단순히 C 어레이를 이용했으니 그 값은 그냥 인덱스로 사용하겠다. 처음 countByEnumeratingWithState... 메소드가 불리면 0이 들어있을 것이다.
처음 불렸을 때 aState->state 안에 돌려 줄 아이템의 갯수를 넣는다. 그리고 itemsPtr에는 아이템이 들어있는 C 어레이를 넣는다. 그리고 mutationsPtr에는 self를 넣어주면 되겠다.
그리고 마지막으로 리턴 값으로는 몇개의 아이템을 넣어보내는지 알려주면 된다.
자, 그럼 뒤에 두 파라메터는 언제 쓰는가?
만일 컬렉션 안에 C 어레이 형태의 스토리지가 없다면, 뒤에 딸려오는 두개의 파라메터를 이용하면 편할 것이다. 먼저 aLen에는 aStackbuf의 크기가 오는데 객체를 aStackbuf안에 최대 aLen만큼 넣어주면 된다.
그리고 aState->state는 어디까지 enumeration했는지에 대한 정보를 aState->itemsPtr에는 aState를 aState->mutationsPtr에는 self를 넣어주면 된다. 그리고 리턴 값은 처음의 경우와 동일하게 몇개의 아이템을 넣어보냈는지 알려주면 된다. 별도의 코드는 작성하지 않겠다.
이것이 별로 마음에 들지 않는 이유 중 하나는 편의를 위해서 랭귀지를 임의로 확장했다는 것이고 다른 하나는 랭귀지의 확장이 랭귀지에서 그치지 않고 프레임웍(NSFastEnumeration)과 연결되어 버렸다는 점이다. 랭귀지의 특정 기능을 이용하기 위해서 더 상위개념인 프레임웍을 반드시 이용해야 하는 상황이 이상한 것은 나 뿐인 것인지 궁금하다.
'개발 > App Developer' 카테고리의 다른 글
구조체(CGPoint,CGSize,CGRect) -> 객체(NSDictionary) -> 구조체 (0) | 2010.09.01 |
---|---|
Window and View (0) | 2010.09.01 |
10.07.06 개발노트 (0) | 2010.08.31 |
10.07.08 스터디 (0) | 2010.08.31 |
10.07.08 GDB 간단 명령어 및 breakpoint 사용법 (0) | 2010.08.31 |