iOS——nil、Nil、NULL和[NSNull null]的区别

iOS——nil、Nil、NULL和[NSNull null]的区别

首先我们先来看一下苹果文档上的解释:

  1. nil:Defines the id of a null instance.(定义空实例的id)
  2. Nil:Defines the id of a null class.(定义空类的id)
  3. NULL:Returns the singleton instance of NSNull.(返回NSNull的单例实例)
  4. 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对象验证一下。

关于空指针与野指针

空指针:

  1. 没有存储任何地址的指针就是空指针
  2. 空指针就是被赋值为0的指针,在没有被具体初始化之前,其值为0。

野指针:

  1. 指向“垃圾”(不可用内存)内存的指针。

总结空指针与野指针:

  1. 利用野指针发消息特别危险,编译器会报错。也就是说,如果一个对象已经被回收了,就不要再去操作它,不要再尝试给它发消息。
  2. 利用空指针发消息是没有任何问题的
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值