OC中的空值(nil、Nil、NULL、NSNull),你用对了么?

空值,也是一种特殊的值。在日常编程中,显而易见的不可或缺,可是,你用对了么?

话不多说,先甩结论:

  • nil : 通常用于表示一个实例对象的空指针,如 id obj = nil;
  • Nil : 通常用于表示一个类对象的空指针,如 Class cls = Nil;
  • NULL : 通常用于表示指向一个非对象类型(基本数据类型、C类型)的空指针,如 char *c = NULL;
  • NSNull : 通常用在集合对象中,表示空值,如 NSArray * arr = @[[NSNull null]];

那么,上面的结论从哪里来的呢?权威的我们还是要从官方入手,比如官方的源码。直达下载

nil

源码中我们能直接找到 nil 的定义:

#ifndef nil
# if __has_feature(cxx_nullptr)
#   define nil nullptr
# else
#   define nil __DARWIN_NULL
# endif
#endif
复制代码

而全局搜索源码 nil 的使用场景,主要就是针对上面提到的,实例对象的空指针。

Nil

同样的,我们也能在源码中找到其定义:

#ifndef Nil
# if __has_feature(cxx_nullptr)
#   define Nil nullptr
# else
#   define Nil __DARWIN_NULL
# endif
#endif
复制代码

发现没有?nilNil 的定义完全相同!为什么呢?

不为什么,两者都是表示的空指针,在数值上就是完全一致的,只是我们从源码的使用场景、代码注释等中可以发现,Nil 主要用于类对象的空指针,这似乎就是官方源码约定俗成的,我们有什么理由不跟上其步伐呢?

NULL

说到 NULL,从源码(stddef.h)来看,定义稍微有点区别:

#undef NULL
#ifdef __cplusplus
#if __cplusplus >= 201103L
#define NULL nullptr
#else
#undef __null  // VC++ hack.
#define NULL __null
#endif
#else
#define NULL ((void*)0)
#endif
复制代码

定义的前面主要是判断是否是C++, OC 里面显然不是,所以始终走的是 #define NULL ((void*)0)。 是不是就是我们熟悉的基本数据类型指针的形式了?

结合其源码的使用场景,NULL 多用在非对象类型的空指针。

稍作总结

实际上,我们注意到,在 nilNil 定义中的 __DARWIN_NULL 实际上就是 NULL :

#ifndef __DARWIN_NULL
#define __DARWIN_NULL NULL
#endif
复制代码

__has_feature(cxx_nullptr)是用来判断是否支持C++11中的nullptr特性的.

nullptrC++ 中的指针空值类型。

到这里是不是都明了了呢?

截止目前,其实我们能看到 nilNilNULL 这三者在某些场景下,其值是一致的,也就是在这些场景下其实是可以混用的,特别是 nilNil ,但是我们平常开发中,最好还是不要这样,一方面这是官方的一种约定俗成,另外也可能会出现不一致而导致的问题。

现在就有一种简单的且清晰明了的规矩,也不会有出错的风险,何乐而不为呢?

NSNull

最后,我们还剩下 NSNull,为什么单独拎出来讲呢?因为它与前面的根本不是一回事。

NSNullNSObject 的一个子类:

@interface NSNull : NSObject <NSCopying, NSSecureCoding>
+ (NSNull *)null;
@end
复制代码

头文件中显示,他有一个类方法,实际上就是获取其单例的方法。

而其使用场景主要是在一些集合对象中,如下这样:

// 数组中使用
NSArray *array = [NSArray arrayWithObjects:
                      [[NSObject alloc] init],
                      [NSNull null],
                      @"aaa",
                      nil,
                      [[NSObject alloc] init],
                      [[NSObject alloc] init], nil];
NSLog(@"%ld", array.count); // 输出 3,NSArray以nil结尾

// 字典中使用
NSDictionary *dictionary = [[NSDictionary alloc] initWithObjectsAndKeys:
                                @"Object0", @"Key0",
                                @"Object1", @"Key1",
                                nil,        @"Key-nil"
                                @"Object2", @"Key2",
                                nil];
NSLog(@"%@", dictionary); // 输出2个key-value,NSDictionary也是以nil结尾

// 可变字典中使用
NSMutableDictionary *mutableDictionary = [[NSMutableDictionary alloc] init];
[mutableDictionary setObject:nil forKey:@"Key-nil"]; // 会引起Crash
[mutableDictionary setObject:[NSNull null] forKey:@"Key-nil"]; // 不会引起Crash
//所以在使用时,如下方法是比较安全的
[mutableDictionary setObject:(nil == value ? [NSNull null] : value)
                      forKey:@"Key"];
复制代码

哦了,就到这里,各种空值的使用姿势都了解了么?希望都能有所收获。

猜你喜欢

转载自juejin.im/post/7078624442816921637