不知道你们有没有发现,在一个 Objective-C 和 Swift 混编的 App 中,当把一个 OC 中的参数转到 Swift 时,Swift 会自动把这个变量进行强制解包。举个例子,我在 OC 中定义这样一个变量:程序员
@property (nonatomic, copy) NSString *foo;
它转成 Swift 就变成了这样:安全
var foo: String!
这样看上去合情合理。Swift 中有 String? 和 String! 两种形式,但 OC 中没有 NSString? 和 NSString! ,当 Swift 没法区分 OC 中的变量是否是 nil 的时候,一概强行转化为非空参数。这样设计体现了 Swift 强安全性语言的特性。app
可是这时候问题来了。咱们回到上文中的例子,假如 OC 中对 foo的使用以下:ide
@property (nonatomic, copy) NSString *foo; - (void)secretFunc { // 一些诡异复杂的操做 ... self.foo = nil; }
而后咱们在 Swift 中这样调用:工具
func init() { objectiveCObject.secretFunc()
}func calcLen() -> Int { atom
return objectiveCObject.foo.characters.countspa
}设计
上面这段 Swift 代码执行到calcLen()时会崩溃,缘由是foo在init()中已经被设成了 nil,而foo在 Swift 中是 foo!。也就是说,由于 Swift 对 OC 变量的强转,致使了程序的崩溃。这是一个很容易忽略的问题,由于强转的时候,Xcode 不会给出任何的警告、报错或是提醒。而咱们做为开发者,很容易忽略这样的错误,致使 runtime 的时候直接崩溃。code
针对这种状况,咱们来讨论下面三个问题。orm
Q: 为何 Swift 要将 OC 中的变量如foo转为foo!而不是foo?
这是一个有争议的话题。我我的认为强制解包的方式会督促开发者考虑变量是否为 nil 的问题。在 OC 时代,声明变量通常不会考虑是否为空的问题;而在 Swift 时代,由于其是一门强安全性的语言,在变量定义时,必须肯定变量是否为空。通常定义为非空有两种如下形式:
// force unwrapping
var foo = "Hello"
// implicitly unwrapping
var foo: String!
前者根据初始值强制解包,定义 foo 为非空变量;后者则直接申明 foo 为非空变量。
不管哪一种状况,开发者会从一开始就思考处理 nil 时的状况,并在后续开发中一直注意。因此从foo转化为foo!,你就会思考 OC 中代码是否也要处理
nil 的状况;而若是转化为foo?,nil 也无所谓,而实际可能并非这样,nil 的特殊状况考虑会一直忽略,开发中的隐患一直存在,同时也不符合 Swift 强安全性的设计思路。
Q: 我就想让 OC 中的变量从foo转化到 Swift 中变成foo?,有没有办法
请这样在 OC 中定义变量:
// foo -> foo?
@property (nullable, nonatomic, copy) NSString *foo;
// bar -> bar!
@property (nonnull, nonatomic, copy) NSString *bar;
// qux -> qux!
@property (nonatomic, copy) NSString *qux;
这种事先声明是否为 null 的定义方法,是否是很像 Swift 中的 optional 机制?然而 OC 时代咱们几乎不会去管变量是否为 nullable 这件事,由此咱们能够体会 OC 和 Swift 在语言设计思路上的差别。
其实nullable和nonnull是 Swift 出来以后才引入 OC 的。因此一开始,OC 中的变量默认都是nullable,转变到 Swift 中,应该就是?。可是这样转化代价太大,咱们全部变量都要在 Swift 中用if else或者guard来解包。因此为了写起来方便,Swift 干脆直接强转,故而如今这个机制也是一个历史遗留问题。
Q: Swift 如此这般致使混编 App 崩溃,没有提示的状况下程序员必须细细检查 nil 致使的 bug,这样设计强制解包的代价是否有点大?
这个 bug 在混编 App 中很容易出现,没有警告确实带来很大困扰。实际上这个问题早就在苹果的开发者论坛上被提出,Swift 组本身也开了个 ticket 要修,惋惜最后不了了之。Github 上有人开发出了第三方的工具来解决这个问题。
我我的以为这个问题苹果不重视的缘由在于 Swift 和 OC 混编只是一个暂时的局面。Swift 取代 OC 是一个时间问题,因此解决混编中的问题都显得没有多大意义,在苹果内部也一直是低优先级。毕竟如今全部精力应该放在 Swift 上,随着时间的推移和 OC 的淡出,这个问题也将微不足道。