可空性与 Objective-C

可空性与 Objective-C

Swift 的一大优点是它能与 Objective-C 代码混编,不论是由 Objective-C 写成的库还是你的应用中的 Objective-C 代码都可以畅通的与 Swift 交互。然而,在 Swift 里可选(optional)的引用与非可选(non-optional)的引用泾渭分明,例如NSView相对于NSView?,在 Objective-C 里这两种类型都对应NSView *。因为 Swift 的编译器判断不出某个NSView *是否是可选值,因此转换为 Swift 的类型就是隐式解包可选类型NSView!

在之前版本的 Xcode 中,有些苹果官方的框架经过了专门的审核,来让 API 正确反映在 Swift 中是否是可选类型。为了支持在你自己的代码里也能实现这种功能,Xcode 6.3 推出了一项新的 Objective-C 语言特性:nullability annotations

核心:nullable 与 nonnull

这项新特性的核心是两个新的类型注解:__nullable__nonnull。正如你所想,标记了__nullable的指针的值有可能为NULLnil,而__nonnull的指针则不可能。如果违反这些规则,编译器会发出警告。

几乎所有能用 C 传统的const关键字的地方都能用__nullable__nonnull,当然只能用在指针类型上。不过,一般情况下有一个更好的方法来使用这两个注解:用在方法声明里时可以不用加下划线,直接写在左括号后面,只需后面的类型是普通对象或 block 的指针。

对于属性,也可以不用加下划线,写在属性特性列表里即可。

不加下划线的形式比加下划线的要好一些,不过你仍然需要在头文件的每个类型前都加一遍注解。想要轻松一些同时让头文件更简洁,你可以使用审核区(audited region)。

审核区

为了循序渐进地采用这些新注解,你可以给 Objective-C 头文件中的一个区块加上“经过非空审核”的标记。在这个区块内部,每个普通的指针类型都会默认为非空。这能让上文的例子大大简化:

出于安全考虑,这条规则有几个例外:

  • typedef 类型一般没有内在的可空性——它们要么为空,要么非空,根据具体的取值而定。因此,即使在审核区内,typedef 类型也不会默认为nonnull
  • 复合指针类型,如id *,必须显式使用注解。例如,指明一个非空的指针指向可空的对象引用,要用__nullable id * __nonnull
  • 一个比较特殊的类型NSError **一般用来在方法参数中返回错误信息,因此它总是默认为可空的指针指向可空的NSError引用。

这方面的更多信息可以参考Error Handling Programming Guide

兼容性

如果现有的代码用到了你的 Objective-C 框架,却与你指定的非空性规则相互抵触怎么办?就这样一下子改变你的类型真的安全吗?放心,是安全的。

  • 现有的编译好的代码如果用到了你的框架,仍然能正常运行,也就是说 ABI 不会变。这也意味着这些现有的代码在运行时不会捕捉到传递 nil 的错误。
  • 现有的源代码如果用到了你的框架,用新的 Swift 编译器编译时,会得到不安全用法的警告。
  • nonnull 不会影响编译优化。尤其是,你仍然可以在运行时检查标注为nonnull的参数,看它实际上是不是nil。这对于向前兼容可能是必要的。

总体来说,基本上你应该像看待 assertion 或 exception 一样看待nullablenonnull:违反这些规则是程序员的错误。尤其是,返回值是你能控制的,所以一定不要对非空的返回类型返回nil,除非是为了向前兼容。

回到 Swift

现在我们已经给 Objective-C 的头文件加了可空性注解,来看看在 Swift 里该怎么用吧:

在给 Objective-C 代码加注解之前:

加了注解之后:

Swift 的代码更清晰了。只是一个微小的改变,却能让你的框架更易于使用。

C 和 Objective-C 的可空性注解在 Xcode 6.3 及以后版本可用。查看更多信息,请参考Xcode 6.3 Release Notes

打赏支持我翻译更多好文章,谢谢!

打赏译者

打赏支持我翻译更多好文章,谢谢!

任选一种支付方式

收藏 评论

关于作者:戴仓薯

iOS开发,同时也是世界上最大的仓鼠。 个人主页 · 我的文章 · 10 ·   

相关文章

可能感兴趣的话题



直接登录
跳到底部
返回顶部