Xcode 6.3中有一项新特性,`nullability`标记,这为 OC 提供了类似 Swift 中的 Optional 的类型 为了让更多开发者从 OC 向 Swift 过渡,OC 和 Swift 混编已经变得尤为重要,就像是OC通向Swift的一座桥梁, 通过引入泛型使得 OC 和 Swift 之间更加安全清楚的理解和共享包含特定元素的集合
The Core: _Nullable and _Nonnull
关于 _Nullable 和 _Nonnull ,官方文档有这么一句话
At the core of this feature we have two new type annotations: _Nullable and _Nonnull.
As you might expect, a _Nullable pointer may have a NULL or nil value, while a _Nonnull one should not.
The compiler will tell you if you try to break the rules.
大概意思就是,我们又添加了两种类型,_Nullable
可以为NULL
或者nil
,反之 _Nonnull
绝对不能为空,否则编译器就会出现警告
@interface AAPLList : NSObject <NSCoding, NSCopying>
// ...
- (AAPLListItem * _Nullable)itemWithName:(NSString * _Nonnull)name;
@property (copy, readonly) NSArray * _Nonnull allItems;
// ...
@end
// --------------
[self.list itemWithName:nil]; // warning!
事实上你可以在任何可以使用C语言关键字const
的地方使用_Nullable
和_Nonnull
当然 _Nullable
和_Nonnull
一般用于指针类型,在一般情况下,有一个更好的方法去实现这种标记的功能
。即在方法申明的时候可以使用non-underscored
形式,nullable
和nonnull
,直接写在类型前面(类型必须是一个对象或者block指针)。
- (nullable AAPLListItem *)itemWithName:(nonnull NSString *)name;
- (NSInteger)indexOfItem:(nonnull AAPLListItem *)item;
在属性中,同样可以使用non-underscored
形式将这样的关键字放到属性列表中
@property (copy, nullable) NSString *name;
@property (copy, readonly, nonnull) NSArray *allItems;
non-underscored
方式比underscored
方式有很明显的优势,但是即便如此,你仍然需要将它们写在每一种类型中去
为了更加高效的工作和代码的简洁,我们需要用到一种苹果称之为audited regions
的方法
什么是 Audited Regions
为了方便采用新的方式,你可以在Objective-C
头文件标记一块区域作为audited for nullability
,在这块区域内,
任何简单指针类型都将被标注为nonnull
类型,这样一来,如果有一大段代码需要加注nonnull
,就比前面那种繁琐的方式简单多了。
NS_ASSUME_NONNULL_BEGIN
@interface AAPLList : NSObject <NSCoding, NSCopying>
// ...
- (nullable AAPLListItem *)itemWithName:(NSString *)name;
- (NSInteger)indexOfItem:(AAPLListItem *)item;
@property (copy, nullable) NSString *name;
@property (copy, readonly) NSArray *allItems;
// ...
@end
NS_ASSUME_NONNULL_END
// --------------
self.list.name = nil; // okay
AAPLListItem *matchingItem = [self.list itemWithName:nil]; // warning!
安全起见,这种方式会有一些异常
1.
typedef
类型不会被当做nonnull
处理,即使在audited regions
内
2.很多复杂的指针,譬如id *
必须明确标注,例如要制定一个非空的指针可以被空对象引用,要这样写_Nullable id * _Nonnull
3.特定类型NSError **
经常通过方法的参数返回错误以致它总是被假定为一个可以被空指针引用的空指针
兼容性
如果已经存在的Objective-C
代码被重写会怎样?单单以这种方式改变类型安全么?没错,是安全的。
现有的编译代码会在你的框架上继续跑,即
ABI
不会变,就是说现有的代码在运行时出现nil
不会报错
当你使用新的Swift
编译器,使用不安全的方式在编译时现有的源代码在你的框架可能会有额外的警告
nonnull
不影响优化,尤其是在运行时你仍然能检查标注为nonnull
的参数是否是nil,向后兼容是很重要的。
这个功能最早被发布在Xcode 6.3
上,关键字是__nullable
和__nonnull
,由于一些三方库潜在的冲突,
在 Xcode 7
上将关键字改为这里提到的_Nullable
和_Nonnull
,为了兼容Xcode 6.3
,苹果定义了
__nullable
和__nonnull
回到Swift
现在我们已经nullability
标注添加到我们的Objective-C
头文件中,来看看怎么在Swift
中使用它
在没有添加标注前的Objective-C
转换成Swift
应该是这样的
class AAPLList : NSObject, NSCoding, NSCopying {
// ...
func itemWithName(name: String!) -> AAPLListItem!
func indexOfItem(item: AAPLListItem!) -> Int
@NSCopying var name: String! { get set }
@NSCopying var allItems: [AnyObject]! { get }
// ...
}
添加之后是这样的
class AAPLList : NSObject, NSCoding, NSCopying {
// ...
func itemWithName(name: String) -> AAPLListItem?
func indexOfItem(item: AAPLListItem) -> Int
@NSCopying var name: String? { get set }
@NSCopying var allItems: [AnyObject] { get }
// ...
}
这样一来,Swift
代码看起来就简洁多了,虽然是一个微妙的变化,但这会让你的框架更完美