OC nullability特性 众所周知,Swift语言是严格区分可选和非可选变量的.比如UIView
和UIView?
.而在之前的OC中却没有这个概念,二者都使用UIView*
表示.由于Swift编译器无法根据UIView*
判断该变量是否是可选的.因此该类型在Swift中被解释为隐式解包UIView!
.而在Swift中对一个值为nil的可选变量进行强制解包时将导致崩溃.因此你必须时刻警惕UIView!
变量在某处会不会被赋值为nil.这不仅带来使用上的不便,而且也影响编程体验.因此有必要让OC兼容Swift的Optional特性,方便OC的类在Swift中的使用.于是在Xcode 6.3 开始官方便给OC添加了nullability功能.
nullability 特性包含_Nullable
和 _Nonnull
这两种修饰符._Nullable
表明该变量可以为nil或NULL值.而_Nonnull
表明该变量不能为nil值.两个变种写法nullable
,nonnull
.
示例如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 #import <Foundation/Foundation.h> #import "Person.h" #define myCase 4 #if myCase == 1 @interface Book : NSObject - (instancetype )initWithName:(NSString *)name; @property (nonatomic , copy ) NSString *name;@property (nonatomic , copy ) NSString *version;@property (nonatomic , assign ) long words;@property (nonatomic , copy ) NSString *publicID;@property (nonatomic , copy ) NSString *url;@property (nonatomic , strong ) Person *author;@end #elif myCase == 2 NS_ASSUME_NONNULL_BEGIN @interface Book : NSObject - (instancetype )initWithName:(NSString *)name; @property (nonatomic , copy ) NSString *name;@property (nonatomic , copy ) NSString *version;@property (nonatomic , assign ) long words;@property (nonatomic , copy ) NSString *publicID;@property (nonatomic , copy ) NSString *url;@property (nonatomic , strong ) Person *author;@end NS_ASSUME_NONNULL_END #elif myCase == 3 NS_ASSUME_NONNULL_BEGIN @interface Book : NSObject - (instancetype )initWithName:(NSString *)name; @property (nonatomic , copy ) NSString *name;@property (nonatomic , copy ) NSString *version;@property (nonatomic , assign ) long words;@property (nonatomic , copy , nullable ) NSString *publicID;@property (nonatomic , copy , nullable ) NSString *url;@property (nonatomic , strong ) Person *author;@end NS_ASSUME_NONNULL_END #elif myCase == 4 @interface Book : NSObject - (nonnull instancetype )initWithName:(NSString * _Nonnull)name; @property (nonatomic , copy ) NSString * _Nonnull name;@property (nonatomic , copy , nonnull ) NSString *version;@property (nonatomic , assign ) long words;@property (nonatomic , strong , nonnull ) NSNumber *barCode;@property (nonatomic , copy , nullable ) NSString *publicID;@property (nonatomic , copy , nullable ) NSString *url;@property (nonatomic , strong , nonnull ) Person *author;@property (nonatomic , strong , nonnull ) NSMutableArray <Person *> *seeker;@end #endif
nullability特性对OC编码的影响 在OC中即使标明为_Nonnull
,你依然可以给它一个nil值,但编译器会给出警告,仅仅是警告.因此在OC中不要对_Nonnull
修饰的变量抱有一定不为nil的幻想,否则该崩还得崩.
nullability特性只是一种约定,在开发的过程中我们要遵守这一约定.如果一个变量是nonnull的,我们在init方法中一定要给它一个初始值,并且在使用过程中不要给它赋值为nil.
nullability特性对OC,Swift混编的影响 nullability特性其实主要还是为了方便OC的类在Swift中的使用.
一个_Nonnull
修饰的OC变量,它在OC代码中可能会被赋值为nil,当它在Swift中使用时nil会变成<uninitialized>
对象.奇怪的是如果是nil的NSString则会被自动转换为空字符""
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 NS_ASSUME_NONNULL_BEGIN @interface Person : NSObject @property (nonatomic, copy ) NSString * name;@property (nonatomic, copy ) NSString * sex;- (void)hello;@end NS_ASSUME_NONNULL_END @interface Book : NSObject - (nonnull instancetype)initWithName:(NSString * _Nonnull)name;@property (nonatomic, copy ) NSString * _Nonnull name;@property (nonatomic, copy , nonnull) NSString * version;@property (nonatomic, assign) long words;@property (nonatomic, strong, nonnull) NSNumber * barCode;@property (nonatomic, copy , nullable) NSString * publicID;@property (nonatomic, copy , nullable) NSString * url;@property (nonatomic, strong, nonnull) Person * author;@property (nonatomic, strong, nonnull) NSMutableArray <Person *> * seeker;@end @objc func printBook () { let name = book.name let version = book.version print (version) let seeker: NSMutableArray = book.seeker seeker.add("sd" ) print (seeker) let num: NSNumber = book.barCode let i = num.intValue let author = book.author let authorName = author.name let publicID = book.publicID ?? "unknown" let url = book.url ?? "unknown" let string = name + "-" + version + "-" + authorName + "-" + publicID + "-" + url author.hello() let des = String .init (describing: author) print ("des:\(des) " ) var arr: [Person ] = [] arr.append(author) print ("\(author) " , "arr个数:\(arr.count) " , arr) print ("\(string) " ) }
打印
1 2 arr个数:1 [] 三国---unknown -unknown
其中的authorName将会是空字符串""
;而author将会是<uninitialized>
,其实就是一个nil对象,给它发送消息没有什么问题,但调用Swift中的某些方法比如print(author)
或type(of: author)
将导致野指针崩溃:Thread 1: EXC_BAD_ACCESS (code=1, address=0x0)
.
OC和Swift语言其他的一些设计差别
Swift中,如果一个变量没有被初始化,它是不能被使用的,编译器会报错.
1 2 3 4 { var peron: Person peron.hello () //Variable 'peron' used before being initialized }
但OC中,声明一个变量后没有给它一个值也能够给它发送消息.
1 2 3 4 5 6 { Person *p; [p hello ]; Class cls = p.class ; NSLog(@"cls:%@" , cls); }
可以看出Swift对初始化是非常严格的.而OC基本没啥限制.
总结 nullability特性只是一种约定,在开发的过程中我们要遵守这一约定.如果一个变量是nonnull的,我们在初始化方法中一定要给它一个初始值,并且在使用过程中不要给它赋值为nil.
OC,Swift混编时,为了使用方便并减少可能的解包崩溃,推荐使用nullability特性,旧的OC代码也有必要添加.
在Swift中尽量不使用强制解包.