Objective-C 리터럴 문법 살펴보기

이 글은 다음 문서를 참고하였음
http://clang.llvm.org/docs/ObjectiveCLiterals.html

애플이 밀고 있는 LLVM 컴파일러 4.0에서부터 Objective-C Literal 이라는 새로운 기능을 사용할 수 있게 되었다. 새롭게 추가된 리터럴은 크게 세 가지로, NSNumber Literals, Colloection Literals, Object Subscripting 이다. 각각을 좀 살펴보자.

NSNumber Literals

Foundation 프레임웍의 NSNumber 클래스는 스칼라값을 감싸는 객체이다. 객체에 들어갈 수 있는 값은 C에서는 ‘수’로 취급할 수 있던 char, short, int, long, long long 등의 정수들과 Float, double과 같은 실수들 그리고 BOOL, bool 값등이다. 이렇게 숫자값을 객체에 감싼 것을 Boxed values라고도 한다더라.

예시

아래는 NSNumber 리터럴에 대한 예들이다.

void main(int argc, const char *argv[]){

    // character literals
    NSNumber *theLetterZ = @'Z';
    // = [NSNumber numberWithChar:'Z'];

    // integral literals
    NSNumebr *fortyTwo = @42;
    // = [NSNumner numberWithInt:42];

    NSNumber *fortyTwoUnsigned = @42U;
    // = [NSNumber numberwithUnsignedInt:42U];

    NSNumebr *fortyTwoLong = @42L;
    // = [NSNumebr numberWithLong:42L];

    NSNumber *fortyTwoLongLong = 42LL;
    // = [NSNumber numberWithLongLong:42LL];

    // floating point literals
    NSNumber *piFloat = @3.141592654F;
    // = [NSNumber numberWithFloat:3.141592654F];

    NSNumber *piDouble = @3.141592654;
    // = [NSNumber numberWithDouble:3.141592654];

    // BOOL
    NSNumber *yesNumber = @YES;
    // = [NSNumber numberWithBOOL:YES];

}

정리하자면 숫자값 앞에 @를 붙이기만하면 NSNumber 객체로 둘러싼 객체를 바로 얻을 수 있다는 것이다.

NSNumber 리터럴은 단순히 숫자값으로 객체를 만드는 것을 넘어서 C수식을 계산하여 그 결과를 사용할 수 있다. 이때는 @( {수식 } )의 형태로 수식을 괄호로 둘러싸주고 그 앞에 @을 붙이면 된다.

NSNumber *smallestInt = @(-INT_MAX - 1);
NSNumber *piOverTwo = @(M_PI / 2);

또한 enum 타입에 대해서도 만들 수 있다.

typedef enum { Red, Green, Blue } Color;
NSNumber *favoriteColor = @(Green);

NSNumber 뿐만 아니라 NSStirng도 동일한 방식으로 C계산식의 결과를 리터럴을 통해 NSString 객체로 만들 수 있다 .

NSString *path = @(getenv("PATH"));
// = [NSString stringWithUTF8String:getenv("PATH")];

Boxed Enum

코코아 프레임워크에서는 종종 특정 상수값들이 enum으로 정의된다. 하지만 이들은 직접적으로 리터럴을 적용받지는 못하는데, 대신에 boxed enum(괄호로 둘러싼)으로 사용할 수는 있다.

enum {
    AVAudioQualityMin = 0,
    AVAduioQualityLow = 0x20,
    AVAudioQualityMedium = 0x40,
    AVAudioQualityHigh = 0x60,
    AVAudioQualityMax = 0x7F
};
 - (AVAudioRecorder *)recordToFile:(NSURL *)fileURL {
    NSDictionary *settings = @{ AVEncodingQualityKey : @(AVAudioQualityMax)};
    return [[AVAudioRecorder alloc] initWithURL:fileURL settings:settings error:NULL];

}

위 코드에서 @(AVAudioQualityMax) 표현식은 AVAudioQualityMax의 정수값으로 치환된다. 만약 enum 타입의 원소들이 확정된 다른 값을 가지고 있는 경우는 다음과 같이 처리한다.

typedef enum : unsigned char { Red, Green, Blue } Color;
NSNumber *red=@(Red), *green = @(Green), *blue = @(Blue);
// = [NSNumber numberWithUnsignedChar:]

혹은

typedef enum : unsigned char { Red, Green, Blue } Color;
Color col = Red;
NSNumber *nsCol = @(col);

Boxed C String

표현식안에 C 문자열을 넣으면 이를 NSString으로 치환한다. 코드는 조금 복잡해 보이지만 잘 보면 어렵지 않게 풀어볼 수 있다.

C 포인터와 함께, 문자열 포인터 표현식은 임의의 포인터 수식과 연결될 수 있다. 따라서 프로그래머는 문자 데이터가 유효하다는 것을 보증해야 한다. (아니면 에러를 뿜으며 종료) 가능한 한 컴파일러는 NULL 문자는 무시하려고 할 것이다.

NSMutableArray *args = [NSMutableArray new];
NSMutableDictionary *options = [NSMutableDictionary new];
while (--argc){
    const char *args = *++argv;
    if (strncmp(arg, "--", 2) == 0) {
        options[@(arg+2)] = @(*++args);
    } else {
        [args addObject:@(arg)];
    }
}

컨테이너 리터럴

Objective-C는 고정 배열이나 사전을 만들기위한 표현식 문법을 추가로 제공한다.

 NSArray *array = [ @"Hello", NSApp, [NSNumber numberWithInt:42] ];

배열은 @[ ]를 통해서 만든다. 각 원소는 “,”를 통해서 구분되며, 마지막에 nil을 넣어줄 필요는 없다. 사전은 아래와 같이 만들 수 있다.

NSDictionary *dictionary = @{
    @"name" : NSUserName(),
        @"date" : [NSDate date],
        @"processInfo" : [NSProcessInfo processInfo]
};

사전은 기존 메시지를 통한 방식과 달리 키 : 값의 구성으로 이루어지며, 마지막에 nil은 붙지 않는다.

객체 치환 (Object substcripting)

다음 코드는 NSMutableArrayNSMutableDictionay 객체의 서브스크립팅을 보여준다. 서브 스크립팅은 기존의 objectAtIndex 메시지를 사용하지 않고 일반 C 배열처럼 배열의 원소에 접근하는 방식을 의미한다.

NSMutableArray * array = @[ ... ];
NSUInteger idx = 3;
id newObject = ..... ;
id oldObject = array[idx];
array[idx] = newOBject; // oldObject를 newObject로 바꾼다.
NSMutableDictionary *dictionary = @{ .... };
NSString *key = @"...";
oldObject = dictionary[key];
dictionary[key] = newObject // 역시 두 객체를 바꿈

배열의 서브 스크립팅은 사실 아래와 같이 동작한다.

NSUInteger idx = 3;
id value = objects[idx];

위 코드는 4번째 객체를 배열로부터 구하는데, 두 번 째 줄은 아래와 완전히 하는 일이 일치하는 듯 하다.

id value = [object objectAtIndexedSubscript:idx];

어떤 인덱스에 대해 새로운 객체로 덮어 쓸 때는

objects[idx] = newValue;

그리고 이것은 아래의 코드로 번역된다.

[object setObject:newValue atIndexedSubscript:idx];

 사전 타입의 서브 스크립팅

id key = ... ;
id value = objects[key];

이는 다시 아래와 같이 번역된다.

id value = [objects objectForKeyedSubscript:key];

enum 은 정수가 나열되는 형태인데, 각 요소가 특정한 타입이 되도록 명시할 수 있다. 이 역시 modern Objective-C의 기능이다.

typedef enum : unsigned char { Red, Green, Blue } Color;

이 때 Color 타입의 변수는 Red, Green, Blue의 값만 가질 수 있고, 각 값은 unsigned char 타입이다.