空值,也是一种特殊的值。在日常编程中,显而易见的不可或缺,可是,你用对了么?
话不多说,先甩结论:
- 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
复制代码
发现没有?nil
跟 Nil
的定义完全相同!为什么呢?
不为什么,两者都是表示的空指针,在数值上就是完全一致的,只是我们从源码的使用场景、代码注释等中可以发现,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
多用在非对象类型的空指针。
稍作总结
实际上,我们注意到,在 nil
、Nil
定义中的 __DARWIN_NULL
实际上就是 NULL
:
#ifndef __DARWIN_NULL
#define __DARWIN_NULL NULL
#endif
复制代码
而 __has_feature(cxx_nullptr)
是用来判断是否支持C++11
中的nullptr
特性的.
nullptr
为 C++
中的指针空值类型。
到这里是不是都明了了呢?
截止目前,其实我们能看到 nil
、Nil
、NULL
这三者在某些场景下,其值是一致的,也就是在这些场景下其实是可以混用的,特别是 nil
与 Nil
,但是我们平常开发中,最好还是不要这样,一方面这是官方的一种约定俗成,另外也可能会出现不一致而导致的问题。
现在就有一种简单的且清晰明了的规矩,也不会有出错的风险,何乐而不为呢?
NSNull
最后,我们还剩下 NSNull
,为什么单独拎出来讲呢?因为它与前面的根本不是一回事。
NSNull
是 NSObject
的一个子类:
@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"];
复制代码
哦了,就到这里,各种空值的使用姿势都了解了么?希望都能有所收获。