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 데이터를 받을 때 의도와 다른 경우가 발생할 수 있다.
NSDictionary
는 nil
을 값으로 가질 수 없기 때문에 키는 있고 값이 없는 경우, [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.가 되었다.