原文:https://www.jianshu.com/p/a197705a44e9
Objective-C为了和Swift兼容,每个属性或每个方法都去指定nonnull
和nullable。
苹果为了减轻我们的工作量,定义了NS_ASSUME_NONNULL_BEGIN和NS_ASSUME_NONNULL_END两个宏。
在这两个宏之间的所有简单指针对象都被假定为nonnull。我们只需要指定那些nullable的指针
在苹果自己的API中, 我们也见过许多的参数的修饰符, 比如说在方法中修饰参数可否为空的标识。今天来总结下
nullable
,nonnull
,__nullable
,__nonnull
,_Nullable
,__Nonnull
之间的区别。作用
首先,
nonnull
,__nonnull
,__Nonnull
这三个修饰的参数是不可以为nil
的。如果参数被它们修饰, 且传入的实参为nil
的话, 编译器将会产生警告。nullable
,__nullable
,_Nullable
这三个修饰的参数是可以为nil
的。如果参数被它们修饰, 且传入的实参为nil
的话, 编译器也不会产生警告。用法
// string 可以为 nil - (void)methodWithString:(nullable NSString *)string {} // string 不可以为 nil - (void)methodWithString:(nonnull NSString *)string {}
nullable
、__nullable
、_Nullable
的区别联系
nullable
、__nullable
、_Nullable
和nonnull
、__nonnull
、__Nonnull
其实是成对的, 所以这里只介绍前者, 后者用法与前者一致。先来看一段代码, 可以考虑下它们之间的区别是什么。
@property (nonatomic, copy, nullable) NSString *string1; @property (nonatomic, copy) NSString * _Nullable string2; @property (nonatomic, copy) NSString * __nullable string3; - (void)methodWithString1:(nullable NSString *)string {} - (void)methodWithString2:(NSString * _Nullable)string {} - (void)methodWithString3:(NSString * __nullable)string {}
其实呢, 这三种写法的效果都是一样的,只是代码摆放的位置不同。
苹果在Xcode 6.3
引入了一个Objective-C
的新特性:Nullability Annotations
,它的核心是两个修饰:__nullable
和__nonnull
。在Xcode 7
中,为了避免与第三方库潜在的冲突,苹果把__nonnull
、__nullable
改成_Nonnull
、_Nullable
。而且苹果也支持没有下划线的写法nonnull
、nullable
,于是就三种写法都可以使用的情况。从上面的代码可以看出,
nullable
修饰于类型前, 但__nullable
和_Nullable
却修饰于类型后。那么它们还有其他的区别呢?
对于方法参数、方法返回值、属性的修饰,可以使用:nonnull
、nullable
或者_Nonnull
、_Nullable
或者__nonnull
、__nullable
。
对于 C函数的参数、Block的参数、Block返回值的修饰,只能使用:_Nonnull
、_Nullable
或者__nonnull
、__nullable
。但是根据苹果的API来说建议弃用__nonnull
、__nullable
。所以应该按照下面的代码来写:
// C函数 - (void)methodWithError1:(NSError * _Nullable * _Nullable)error {} // Block 返回值 - (void)methodWithBlock1:(void(^ _Nullable)(void))block {} - (void)methodWithBlock2:(void(^ __nullable)(void))block {} // 注意下面的 nullable 用于修饰传入的参数 block 可以为空,而不是修饰 block 返回值 - (void)methodWithBlock3:(nullable void(^)(void))block {}
// Block返回值 和 Block参数 - (void)methodWithBlock4:(NSString * __nonnull(^ __nullable)(NSString * __nullable params))block {} - (void)methodWithBlock5:(NSString * _Nonnull (^ _Nullable)(NSString * _Nullable params))block {} // 注意下面的 nullable 用于修饰方法传入的参数 block 可以为空,而 __nonnull 用于修饰 block 返回值 NSString 不能为空; - (void)methodWithBlock6:(nullable NSString * __nonnull(^)(NSString * __nullable params))block {}
拓展 Nonnull Audited Regions
有两个宏
NS_ASSUME_NONNULL_BEGIN
和NS_ASSUME_NONNULL_END
。在这两个宏之间的代码,所有简单指针对象都被认为是nonnull
修饰的 ,所以我们只需要指定nullable
的指针对象即可。NS_ASSUME_NONNULL_BEGIN - (void)methodWithString4:(NSString *)str string:(nullable NSString *)string {} NS_ASSUME_NONNULL_END
如上写的话, 参数
str
为nonnull
的, 而参数string
为nullable
的。总结
对于方法参数、方法返回值、属性的修饰,可以使用:
nonnull
、nullable
或者_Nonnull
、_Nullable
或者__nonnull
、__nullable
。
对于 C函数的参数、Block的参数、Block返回值的修饰,只能使用:_Nonnull
、_Nullable
或者__nonnull
、__nullable
。但是根据苹果的API来说建议弃用__nonnull
、__nullable
。Demo下载地址:
Demo
参考资料:
Difference between nullable, __nullable and _Nullable in Objective-C
1,nonnull和nullable声明属性是否可以为nil,如果对nonnull属性赋值为nil,则会报警告。
1
2
3@property (nonatomic, strong, nonnull) NSArray *array1;
@property (nonatomic, strong) NSArray * __nullable array2;作用主要是开发者之间交互,调用属性时可以知道这个属性是可能为nil的,针对这个状态做些处理。默认情况下就是nullable状态。可以用来给方法的参数最要求,比如定一个方法,参数为一个不为空的对象:2,null_resettable声明属性set方法可以为nil,但是get一定不能为nil,这里有一个貌似冲突的地方,就是可以定义个属性为nil,但是在获取这个属性的时候,这个属性不能为nil,否则会有警告。这里用到的地方例如在UIViewController中的view,意味着开发者可以对控制器的view清空赋值为nil,但是控制器view是懒加载的,每次获取控制器的view时候,都可以再获取到非nil的view。一个Objective-C的新特性:nullability annotations。这一新特性的核心是两个新的类型注释:__nullable和__nonnull。从字面上我们可以猜到,__nullable表示对象可以是NULL或nil,而__nonnull表示对象不应该为空。当我们不遵循这一规则时,编译器就会给出警告。不过这只是一个警告,程序还是能编译通过并运行。事实上,在任何可以使用const关键字的地方都可以使用__nullable和__nonnull,不过这两个关键字仅限于使用在指针类型上。而在方法的声明中,我们还可以使用不带下划线的nullable和nonnull
一. 为什么要用@NonNull?
NullPointerException空指针异常(俗称NPE异常)可以说是每一个开发者都遇到过的一个常见异常,即使是经验丰富的老手,也会在一不留神的写出NPE的bug。指针只存在于c语言中,Java中是没有指针的,空指针就是空引用,java空指针异常就是引用本身为空,却调用了方法,这个时候就会出现空指针异常。可以理解,成员变量和方法是属于对象的(除去静态),在对象中才存在相对应的成员变量和方法,然后通过对象去调用这些成员变量和方法。对于空指针来说,它不指向任何对象,也就没有所谓的成员变量和方法,这个时候用它去调用某些属性和方法,当然会出现空指针异常。
新建Student类,并创建全参构造函数,getter和setter方法。public class Student { private String name; private Integer age; public Student(String name, Integer age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } }
我们new了一个name为null,age为18的student对象。可以看到当程序通过student.getName()访问name变量后,再调用String的实例方法length(),控制台抛出了NullPointerException异常。这是因为name的变量值为null,所以报NPE异常。解决这类问题的办法,我们可以在取出name值后进行判空操作,但是这样每次取name值都要进行判空操作,很是麻烦。我们可以在构造函数中要求name值非空:public Student(String name, Integer age) { if (name == null) { throw new NullPointerException("name is null"); } else { this.name = name; this.age = age; } }
这样一来,我们在new的时候就会提前发现错误。new的时候就会报NullPointerException,避免后面的空指针异常。new时候的空指针异常是不可能避免的,这是由于Java中规定null可以为任何包装类型的变量赋值。这个判空的逻辑体可以使用Lombok的注解@NonNull进行简化代码。
二. @NonNull如何使用?
在个构造函数的参数加上@NonNull后,便会自动对该参数值进行判空。
public Student(@NonNull String name, Integer age) { this.name = name; this.age = age; }
编译该工程,打开编译后的文件。可以看到,编译器已经为形参name生成了判空的代码。
三. @NonNull源码
package lombok; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * If put on a parameter, lombok will insert a null-check at the start of the method / constructor's body, throwing a * {@code NullPointerException} with the parameter's name as message. If put on a field, any generated method assigning * a value to this field will also produce these null-checks. * 如果放在一个参数上,lombok将在方法/构造函数体的开头插入一个空检查,抛出一个{@code NullPointerException},参数名为message。如果放 * 在字段(成员变量)上,则生成的任何方法都被赋值该字段的值也将产生这些空值检查。 */ @Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.LOCAL_VARIABLE, ElementType.TYPE_USE}) @Retention(RetentionPolicy.CLASS) @Documented public @interface NonNull { }
四. 特别说明
本文已经收录在Lombok注解系列文章总览中,并继承上文中所提的特别说明。
源码地址:gitee
原文:https://www.jianshu.com/p/a197705a44e9
Objective-C为了和Swift兼容,每个属性或每个方法都去指定
nonnull
和nullable。
苹果为了减轻我们的工作量,定义了NS_ASSUME_NONNULL_BEGIN和NS_ASSUME_NONNULL_END两个宏。
在这两个宏之间的所有简单指针对象都被假定为nonnull。我们只需要指定那些nullable的指针
转载于:https://www.cnblogs.com/liuyongfa/p/10185084.html