iOS的一些关键字
最近在使用Swift的过程中,感觉到苹果公司为了迎合Swift,在Objective-C中添加了许多关键字。这些关键字一般用来用来修饰属性,或者方法的参数以及方法的返回值等等。而在以前的Objective-C开发中我们可能并不会去关注这样的一些关键字。尽管iOS10就要出了,但出于更深入了解两种语言的目的,我总结了一些Objective-C的新关键字。
新关键字的出现总是有其意义的,而iOS9的这些关键字可以很清晰的看出苹果公司推出的意义:
1.迎合Swift
因为Objective-C是一个弱类型的语言,程序在运行时才会去确认每个对象的类型与声明的类型是否一致。而Swift是强类型的语言,需要我们在编译时确定每一个对象的类型。这样两种语言在处理同一个方法或属性的时候可能会出现矛盾。为了迎合Swift,苹果公司添加了许多语言来兼容两种语言。
2.提高我们开发人员的开发规范,减少程序员之间的交流
我们以前可能没有规定全面一个属性的所有方面的需求,这样在遇到实际的需求的时候两个程序员对同一个接口的参数可能有歧义,不清楚是否可以为空等等。规定全面了这些问题后,我们在敏捷开发中就可以省去这部分不必要的交流,更多的时间与精力放在编程中。
一.nullable
这个关键字由两部分组成,与接下来的很多关键字是相同的。null:空的;able:可以。也就是说,nullable修饰的属性是可以为空的。
nullable有三种不同的书写规范,分别放在不同的位置,但表达的意思是相同的。
1 // 方式一: 2 @property (nonatomic, strong, nullable) NSString *nullableName; 3 4 // 方式二:放在*号之后 5 @property (nonatomic, strong) NSString * _Nullable _NullableName; 6 7 // 方式三:beta版本的关键字,手写的时候已经没有提示了 8 @property (nonatomic, strong) NSString * __nullable __nullableName;
简单来说,1.不带下划线的全小写nullable,放在括号内;2.带一个下划线的首字母大写_Nullable,放在星号*后;3.带两个下划线的全小写__nullable,放在星号*后。
之后的许多关键字都有这三种写法。不再一一赘述。
我们在调用的时候,系统会从最基本的提示变更为带有_Nullable可以为空提示的字样:
这样,我们是可以给这个属性设置为空的:
1 [self setName:nil];
既然一开始我们就提到了这些关键字是为了迎合Swift,那这个关键字体现在Swift中是什么呢?那就是 "?" 这个标识符了。这个标识符在Swift中代表这个常量/变量是可能为空的,对应到Objective-C中,自然就是nullable。
二.nonnull
non:非,不;null:空的。nonnull:非空的。也就是说,nonnull修饰的属性与上面nullable不同,是不能为空的。
nonnull同样有三种不同的书写格式:
1 方式一: 2 3 @property (nonatomic, strong, nonnull) NSString *nonnullIcon; 4 5 6 7 方式二:放在*号之后 8 9 @property (nonatomic, strong) NSString * _Nonnull _NonnullIcon; 10 11 12 13 方式三:beta版本的关键字,已经没有提示 14 15 @property (nonatomic, strong) NSString * __nonnull __nonnullIcon;
与上面类似,不再赘述。
使用时,也与nullable有相似的效果:
我们在给标识为nonnull的属性设置为空时,系统会提示我们出错:
Warning:Null passed to a callee that requires a non-null argument. // callee:传话者,被召者,这里指代属性; argument:参数。
警告:我们将一个空值传给了一个要求非空参数的属性。
同样的,nonnull对应在Swift中,就是 "!" 标识符了,代表非空的。
三.注意:iOS9.0新出的关键字只能修饰对象,不能修饰基本数据类型
我们刚才试验的对象,都是字符串对象,NSString类型。那么我们可不可以用上述关键字修饰基本数据类型呢?我们可以试一下。
提示报错。
Alert:Nullability specifier 'nonnull' cannot be applied to non-pointer type 'int'. // specifier:分类符; applied:应用的,实用的
警告:表示非空的标识符"nonnull"不能适用于非指针类型'int'。
也就是说,新推出的关键字,只能修饰对象,不能修饰基本数据类型。
我们刚才都是在属性中使用关键字。而文章一开头就说明了,这些关键字除了用来修饰属性之外,还可以修饰方法的参数与返回值。那么这样是如何使用呢?
使用方式有两种,并不像之前的三种了:
1 - (nullable NSString *)test:(nullable NSString *)str; 2 - (NSString *_Nullable)test1;
而在调用过程中是这样的:
这样,我们一眼就能看明白这个参数或返回值是可以为空或非空的,就不会给他传空等等造成沟通的麻烦,也就体现了我之前提到的,减少不必要的交流的问题。
我们学习了nullable和nonnull之后,会发现我们几乎碰到所有系统类的属性与方法中都带着这两个之一的关键字。那么苹果官方是怎么写的呢?难道一个属性一个属性的添加这样的关键字吗?我们点进去UIButton,发现很多属性都带有nullable,而几乎看不到nonnull关键字。难道这些关键字都可以为空吗?
其实并不是这样。在许多类中,我们都会在开头和结尾看到这样两个宏的出现:
1 #define NS_ASSUME_NONNULL_BEGIN _Pragma("clang assume_nonnull begin") // assume:确认 2 #define NS_ASSUME_NONNULL_END _Pragma("clang assume_nonnull end")
在NS_ASSUME_NONNULL_BEGIN和NS_ASSUME_NONNULL_END两个宏之间,定义的所有对象属性和方法默认为nonnull。
我们可以尝试一下:
1 NS_ASSUME_NONNULL_BEGIN 2 3 @property (nonatomic, strong) NSString *name; 4 5 NS_ASSUME_NONNULL_END
这样,在调用这个属性的时候我们会发现:
1 [self setName:<#(NSString * _Nonnull)#>];
这个属性是默认非空的。
也就是说,我们也可以在我们的类中的开头和结尾使用这两个宏,然后我们只需要给可能为空的属性添加nullable就可以了。
六.null_resettable
null:空的; resettable:可重调; null_resettable:可以重新设置为空的。
这个关键字表示,这个属性getter方法不能返回为空;setter可以为空。如何会出现这种情况呢?也就是说,我们必须要重写这个属性的getter方法或setter方法来处理传值为空的情况,否则就会出现警告。
Warning:Synthesized setter 'setNull_resettableName:' for null_resettable property 'null_resettableName' does not handle nil.
警告:同步setter方法'setNull_resettableName:' 给可以设置为空的属性'null_resettableName'时没有经过处理。
这种典型的,就是我们在调ViewController的view时,系统将view的getter方法给重写了。大致是这样的:
1 @synthesize view = _view; 2 3 - (UIView *)view { 4 5 if (!_view) { 6 7 [self loadView]; 8 [self viewDidLoad]; 9 } 10 11 return _view; 12 }
同样的,我们也可以用同样的方式对null_resettable的属性进行处理:
1 @synthesize null_resettableName = _null_resettableName; 2 // 重写get 3 - (NSString *)null_resettableName { 4 5 if (!_null_resettableName) { 6 7 _null_resettableName = @""; 8 } 9 10 return _null_resettableName; 11 } 12 13 - (void)setNull_resettableName:(NSString *)null_resettableName { 14 15 if (!null_resettableName) { 16 17 _null_resettableName = @""; 18 } 19 20 _null_resettableName = null_resettableName; 21 }
因为我同时重写了setter与getter方法,所以编译器需要找到属性null_resettableName的变量形式是什么。实际编程中,我们重写这两个方法中的任意一个就可以了。
七.null_unspecified
null:空的; unspecified:未特别指出的;未具体说明的; 也就是说null_unspecified是我们未具体指出是否为空,也就是默认状态。或者说,这个关键字写不写都行。
与之类似的,它有三种书写方式:
1 @property (nonatomic, strong, null_unspecified) NSString *null_unspecifiedName; 2 @property (nonatomic, strong) NSString *_Null_unspecified _Null_unspecifiedName; 3 @property (nonatomic, strong) NSString * __null_unspecified __null_unspecifiedName;
调用时,是这样的:
1 [self setNull_unspecifiedName:<#(NSString * _Null_unspecified)#>]; 2 [self set_Null_unspecifiedName:<#(NSString * _Null_unspecified)#>]; 3 [self set__nullableName:<#(NSString * _Nullable)#>];
希望大家有所收获。