Optional Unwrapping
2017년 1월 22일

NSDictionary의 키에 값이 있는지 확인하기

Objective-C에서 NSDictionary의 특정 키에 해당하는 값이 있는지 확인해야 할 때가 있다. 주로 서버에서 가져온 JSON 데이터에서 값을 추출할 때 필요하다.

전통적인 권장 방법은 objectForKey 메서드를 사용하는 것이다.

NSDictionary<NSString *, NSString *> *married = @{@"Sam" : @YES,
                                                  @"John" : @YES,
                                                  @"Kevin" : @YES,
                                                  @"William" : @NO};
NSString *key = @"John";
if ([married objectForKey:key]) {
    NSString *log = [key stringByAppendingString:@" is a member."];
    NSLog(@"%@", log);
} else {
    NSString *log = [key stringByAppendingString:@" is not a member."];
    NSLog(@"%@", log);
}

이 방법을 사용하면 크래시 없이 키에 값이 있는지 확인할 수 있다.

현대적인 Objective-C에서는 서브크립팅([]) 문법으로 조금 더 편하게 사용할 수 있다.

// 중복 코드 생략
if (married[key]) {
    NSString *log = [key stringByAppendingString:@" is a member."];
    NSLog(@"%@", log);
} else {
    NSString *log = [key stringByAppendingString:@" is not a member."];
    NSLog(@"%@", log);
}

첫 예제와 같은 동작을 하는 코드로 문법만 다르기 때문에 마찬가지로 크래시 없이 사용할 수 있다.

JSON 데이터와 NSNull

다만, JSON 데이터를 받을 때 의도와 다른 경우가 발생할 수 있다.

NSDictionarynil을 값으로 가질 수 없기 때문에 키는 있고 값이 없는 경우, [NSNull null]이 값으로 할당되고는 한다. Objective-C에서 모든 객체는 조건문에서 true로 평가 되기 때문에 [NSNull null] 역시 true로 평가된다. @0 혹은 @NO 역시 마찬가지이다. 따라서 값이 [NSNull null]인 키는 조건문에서 값이 있다고 평가된다. 하지만 일반적으로는 값이 없다고 평가되어야 올바른 로직을 구현할 수 있다. 그래서 보통 다음과 같은 매크로를 사용한다.

#define isNull(value) value == nil || [value isKindOfClass:[NSNull class]]

NSDictionary<NSString *, NSString *> *married = @{@"Sam" : @YES,
                                                  @"John" : [NSNull null],
                                                  @"Kevin" : @YES,
                                                  @"William" : @NO};
NSString *key = @"John";
if (isNull(married[key])) {
    NSString *log = [key stringByAppendingString:@" is a member."];
    NSLog(@"%@", log);
} else {
    NSString *log = [key stringByAppendingString:@" is not a member."];
    NSLog(@"%@", log);
}

Swift

Swift에서는 어떻게 할까? Swift의 Dictionary 타입은 nil을 값으로 가질 수 있으므로 다음과 같은 코드를 만들었다.

let married = ["Sam": true,
               "John": nil,
               "Kevin": true,
               "William": false]
let key = "John"
if married[key] != nil {
    print("\(key) is a member.")
} else {
    print("\(key) is not a member.")
}

결과는 예상과 달리 John is a member. 다. 문제가 뭘까? married의 타입을 확인하면 [String : Bool?]이다. 값이 옵셔널 Bool이기 때문에 문제가 생겼다. 옵셔널을 언랩해야만 원하는 결과를 얻을 수 있다.

// 중복 코드 생략
if let value = married[key], value != nil {
    print("\(key) is a member.")
} else {
    print("\(key) is not a member.")
}

이제 결과는 John is not a member.가 되었다.


참고