NULL이라함은 보통 "값이 없음"을 말한다.
C언어에서 NULL은 사실 그냥 0으로 정의되어 있는데, 포인터변수에 NULL을 사용하여 레퍼런스하는 대상이 없다는 의미로 사용한다. Objective-C에서는 객체 포인터 타입인 id에서 사용하기 위해 따로 nil을 정의하고 있다. nil도 사실 0으로 정의되어 있기 때문에 이넘이나 그넘이나 같은 넘이다.
Objective-C가 C나 C++과 다른 점 중에 하나는 이 nil이 단순히 레퍼런스하는 객체가 없다는 의미 외에도 "nil object"라고까지 표현할만큼 특별한 기능(?)을 발휘하고 있다는 점이다. 바로 어떠한 메세지도 받아들이고(respond) 결과로 자기 자신을 리턴한다는 것이다.(자세한 내용은 여기참조) 즉, 다음과 같은 코드도 런타임에서 어떠한 에러도 발생하지 않고 잘 실행된다. 물론 아무것도 하지 않지만.
[nil killYourself];
Java나 Smalltalk도 예외를 발생시키지만, Objective-C는 그렇지 않다. 이런 특별한 기능(?)이 복잡한 예외처리를 필요로 하는 경우나 디버깅할 때 귀찮을 경우도 있지만, 코드를 단순화시키기 쉽고 잘 죽지 않는(허허.. 이거 좋은 건지...) 프로그램을 만들 수 있다... (응?)
그런데....
NSNull 이라는 것도 있다. 코코아의 컬렉션 객체들이 nil 즉, NULL 포인터를 저장할 수 없기 때문에 존재하는, nil은 아닌 Null 객체다. (헉.. 이건, 뭐...) 문제는 NSNull 객체는 nil이 가지고 있는 특별한 기능(?)이 없다는 것이다. 즉, 다음과 같은 코드는 시키는 대로 아주 잘 죽는다... 쩝.
[[NSNull null] killYourself];
그래서, nil과 NSNull 객체가 둘 다 들어올 수 있는 코드에서는 객체 포인터를 미리 잘 확인해봐야 한다. 다음과 같이...
if (anObj && ![anObj isKindOfClass:[NSNull class]])
{
// 이제 anObj를 마음껏 사용한다.
}
매번 if문을 저렇게 쓰는 것이 싫다면 nil이나 NSNull 객체가 아닌지 확인하는 메소드를 다음과 같이 구현할 수 있겠다.
@interface NSObject (NullTesting)
- (BOOL)isNotNull;
@end
@implementation NSObject (NullTesting)
- (BOOL)isNotNull
{
return [self isKindOfClass:[NSNull class]] ? NO : YES;
}
@end
또는, 이렇게.
@interface NSObject (NullTesting)
- (BOOL)isNotNull;
@end
@implementation NSObject (NullTesting)
- (BOOL)isNotNull
{
return YES;
}
@end
@implementation NSNull (NullTesting)
- (BOOL)isNotNull
{
return NO;
}
@end
그러면, 다음과 같이 if 문의 조건을 쉽게 쓸 수 있다.
if ([anObj isNotNull])
{
// 이제 anObj를 마음껏 사용한다.
}
주의할 점은 isNull 메소드는 불가능하다. 왜냐하면 nil은 항상 nil(0)만 리턴하기 때문이다.
if문조차 싫다면 Objective-C의 message forwarding을 이용해서 NSNull이 nil 처럼 모든 메세지를 무시하도록 만들 수 있겠다.
@implementation NSNull (NilObject)
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
return [NSMethodSignature signatureWithObjCTypes:"@@:"];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
id ret = nil;
[anInvocation setReturnValue:&ret];
}
@end
이제는 좀 마음이 편해질려나...
이 글은 스프링노트에서 작성되었습니다.


