iOS——nil、Nil、NULL和[NSNull null]的区别
首先我们先来看一下苹果文档上的解释:
- nil:Defines the id of a null instance.(定义空实例的id)
- Nil:Defines the id of a null class.(定义空类的id)
- NULL:Returns the singleton instance of NSNull.(返回NSNull的单例实例)
- NSNull:A singleton object used to represent null values in collection objects that don’t allow nil values.(用于表示不允许空值的集合对象中的空值的单例对象)
区别
先看一下它们的打印值:
NSLog(@"%@",nil);
NSLog(@"%@",Nil);
NSLog(@"%@",NULL);
NSLog(@"%@",[NSNull null]);
我们可以看到,前三个的打印值是一样的,而NSNull的打印值不太一样
nil
首先我们先来看nil
定义
#ifndef nil
# if __has_feature(cxx_nullptr)
# define nil nullptr
# else
# define nil __DARWIN_NULL
# endif
#endif
其中__has_feature(cxx_nullptr)用于判断C++中是否有nullptr特性,对于普通iOS开发者来说,nil的定义形式为:
# define nil __DARWIN_NULL
就是说nil最终是__DARWIN_NULL的宏定义, __DARWIN_NULL是定义在_types.h中的宏,其定义形式如下:
#ifdef __cplusplus
#ifdef __GNUG__
#define __DARWIN_NULL __null
#else /* ! __GNUG__ */
#ifdef __LP64__
#define __DARWIN_NULL (0L)
#else /* !__LP64__ */
#define __DARWIN_NULL 0
#endif /* __LP64__ */
#endif /* __GNUG__ */
#else /* ! __cplusplus */
#define __DARWIN_NULL ((void *)0)
#endif /* __cplusplus */
非C++代码的 __DARWIN_NULL最终定义形式如下:
#define __DARWIN_NULL ((void *)0)
所以 *nil本质上是:(void )0
用处
用于表示指向Objective-C中对象的指针为空
NSString *string = nil;
id anyObject = nil;
Nil
定义
#ifndef Nil
# if __has_feature(cxx_nullptr)
# define Nil nullptr
# else
# define Nil __DARWIN_NULL
# endif
#endif
nil和Nil一样,*Nil本质上也是:(void )0
用处
用于表示Objective-C类(Class)类型的变量值为空
Class anyClass = Nil;
NULL
定义
#undef NULL
#ifdef __cplusplus
# if !defined(__MINGW32__) && !defined(_MSC_VER)
# define NULL __null
# else
# define NULL 0
# endif
#else
# define NULL ((void*)0)
#endif
其中__cplusplus表示是不是C++代码,所以对于普通的iOS开发者来说,通常NULL的定义就是:
# define NULL ((void*)0)
所以,NULL本质上是:(void)0* NULL是C语言发展来的,所以表示的就是空指针
用处
NULL表示C指针为空
charchar *string = NULL;
NSNull
@interface NSNull : NSObject <NSCopying, NSSecureCoding>
+ (NSNull *)null;
@end
用处
从定义中可以看出,NSNull是一个Objective-C类,只不过这个类相当特殊,因为它表示的是空值,即什么都不存。它也只有一个单例方法+[NSNUll null]。该类通常用于在集合对象中保存一个空的占位对象。
我们通常在定义NSArray数组时:
NSArray *arr = [NSArray arrayWithObjects:@"wang",@"zhang",nil];
在NSArray中只要遇到了nil,NSArray就会认为数组元素该截止了,即NSArray只关注nil之前的对象,nil之后的对象会被抛弃。比如下面的写法:
NSArray *arr = [NSArray arrayWithObjects:@"wang",@"zhang",nil,@"zhi"];
此时,NSArray就只会保存前两个元素,nil后面的元素就会被舍弃
这种情况,就可以使用NSNull实现:
NSArray *arr = [NSArray arrayWithObjects:@"wang",@"zhang",[NSNull null],@"zhi"];
那么向nil和NSNull发送消息会产生什么样的影响呢?
首先向nil发送消息程序是不会崩溃的
因为OC的函数都是通过objc_msgSend进行消息发送来实现的,相对于C和C++来说,对于空指针的操作会引起crash问题,而objc_msgSend会通过判断self来决定是否发送消息,如果self为nil,那么selector也会为空,直接返回,不会出现问题。视方法返回值,向nil发消息可能会返回nil(返回值为对象),0(返回值为一些基础数据)或0X0(返回值为id)等。
但是如果向NSNull发送消息,那么程序就会崩溃了
对于[NSNull null]对象发送消息时,是会crash的,因为NSNull类只有一个null方法。
关于对nil发送消息unrecognized selector sent to instance
会造成程序崩溃的原因:
这是因为这个对象已经被释放了(引用计数为0了),那么这个时候再去调用方法肯定是会Crash的,因为这个时候这个对象就是一个野指针(指向僵尸对象(对象的引用计数为0,指针指向的内存已经不可用)的指针)了,安全的做法是释放后将对象重新置为nil,使它成为一个空指针,大家可以在关闭ARC后手动release对象验证一下。
关于空指针与野指针
空指针:
- 没有存储任何地址的指针就是空指针
- 空指针就是被赋值为0的指针,在没有被具体初始化之前,其值为0。
野指针:
- 指向“垃圾”(不可用内存)内存的指针。
总结空指针与野指针:
- 利用野指针发消息特别危险,编译器会报错。也就是说,如果一个对象已经被回收了,就不要再去操作它,不要再尝试给它发消息。
- 利用空指针发消息是没有任何问题的