天天看点

Object-C中nil, NULL跟NSNull

相信不少开发者,都被NSNull坑过,最常见的是服务器返回的json里面,说好的字典、数组、数字,结果返回的是空值。   这个时候,NSJSONSerialization 会自动把他们换成 NSNull。当我们再去用dict[@“hello”]的时候,就会出触发exception,导致程序崩溃。   那么如何处理它呢? 我曾经的做法 写了个宏,判断返回的这个类是不是NSNull类,即 isKindOfClass   最简单的做法 相信大家都知道,[NSNull null] 并不是一个 工厂方法,而是一个 单例模式,那么我们直接去判断赋值的这个指针是不是[NSNull null] 就好了。   那么问题来了,编译器会多了一个warning,很烦人。   在 这篇文章里面介绍了各种做法:

  1. - (void)someMethod 
  2.     NSString *aString = @"loremipsum"; 
  3.     // This will complain: "Comparison of distinct pointer types ('NSString *' and 'NSNull *')" 
  4.     if (aString != [NSNull null]) 
  5.     { 
  6.     } 
  7.     // This works (at least for strings), but isEqual: does different things  
  8.     // for different classes, so it's not ideal 
  9.     if ([aString isEqual:[NSNull null]]) 
  10.     { 
  11.     } 
  12.     // If you cast it to the class you're comparing against 
  13.     // then you're good to go 
  14.     if (aString != (NSString *)[NSNull null]) 
  15.     { 
  16.     } 
  17.     // But we can also just cast it to id and 
  18.     // that works generically 
  19.     if (aString != (id)[NSNull null]) 
  20.     { 
  21.     } 
  22.     // The thing that would be really cool, 
  23.     // would be [NSNull null] returning 
  24.     // id (like in the sample category below). 
  25.     // Wouldn't count on that one though. 
  26.     if (aString != [NSNull idNull]) 
  27.     { 
  28.     } 

  这些都不是非常漂亮的解决方案,这篇文章的作者推荐:

  1. @interface NSNull (idNull) 
  2. + (id)idNull; 
  3. @end 
  4. @implementation NSNull (idNull) 
  5. + (id)idNull { return [NSNull null]; } 
  6. @end 

  或者呢

  1. if ([[NSNull null] isEqual:aString]) 

  最终解决方案 上面的做法,都需要判断一次,还是很不优雅,为什么呢,我们还是不能像NULL,nil一样,直接拿来用,还是需要判断一下,这里推荐一套最漂亮的作法。   陈航提供了一个 gist   我发现我的octopress的gist插件挂了,直接贴出来好了。

  1. #define NSNullObjects @[@"",@0,@{},@[]] 
  2. @interface NSNull (InternalNullExtention) 
  3. @end 
  4. @implementation NSNull (InternalNullExtention) 
  5. - (NSMethodSignature*)methodSignatureForSelector:(SEL)selector 
  6.     NSMethodSignature* signature = [super methodSignatureForSelector:selector]; 
  7.     if (!signature) { 
  8.         for (NSObject *object in NSNullObjects) { 
  9.             signature = [object methodSignatureForSelector:selector]; 
  10.             if (signature) { 
  11.                 break; 
  12.             } 
  13.         } 
  14.     } 
  15.     return signature; 
  16. - (void)forwardInvocation:(NSInvocation *)anInvocation 
  17.     SEL aSelector = [anInvocation selector]; 
  18.     for (NSObject *object in NSNullObjects) { 
  19.         if ([object respondsToSelector:aSelector]) { 
  20.             [anInvocation invokeWithTarget:object]; 
  21.             return; 
  22.         } 
  23.     } 
  24.     [self doesNotRecognizeSelector:aSelector]; 
  25. @end 

  很高端霸气上档次的做法,通过处理异常情况,来实现这个功能。   这里还提供一个 日本人的封装方案:

  1. #import "NSNull+OVNatural.h" 
  2. @implementation NSNull (OVNatural) 
  3. - (void)forwardInvocation:(NSInvocation *)invocation 
  4.     if ([self respondsToSelector:[invocation selector]]) { 
  5.         [invocation invokeWithTarget:self]; 
  6.     } 
  7. - (NSMethodSignature *)methodSignatureForSelector:(SEL)selector 
  8.     NSMethodSignature *sig = [[NSNull class] instanceMethodSignatureForSelector:selector]; 
  9.     if(sig == nil) { 
  10.         sig = [NSMethodSignature signatureWithObjCTypes:"@^v^c"]; 
  11.     } 
  12.     return sig; 
  13. @end 

  关于[NSMethodSignature signatureWithObjCTypes:“@^vc”]的功能,可以参考以下两篇文章: https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html http://nshipster.com/type-encodings/

//判断对象不空
if(object) {}

//判断对象为空
if(object == nil) {}

//数组初始化,空值结束
NSArray *pageNames=[[NSArray alloc] initWithObjects:@"DocumentList",@"AdvancedSearch",@"Statistics",nil];

//判断数组元素是否为空
UIViewController *controller=[NSArray objectAtIndex:i];
if((NSNull *)controller == [NSNull null])
{
    //
}

//判断字典对象的元素是否为空
NSString *userId=[NSDictionary objectForKey:@"UserId"];
if(userId == [NSNull null])
{
    //
}