从强制解包看 Swift 的设计

Posted 二进制小镇

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从强制解包看 Swift 的设计相关的知识,希望对你有一定的参考价值。

作者: 故胤道长

福利 - 扩展班往期视频 已经共享群内

若无法下载请联系微信: yanzy310

不知道大家有没有发现,在一个 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 强安全性语言的特性。

但是这时候问题来了。我们回到上文中的例子,假如 OC 中对 foo的使用如下:

@property (nonatomic, copy) NSString *foo;

- (void)secretFunc {  
 // 一些诡异复杂的操作  ...  
 self.foo = nil; }

然后我们在 Swift 中这样调用:

func init() {
  objectiveCObject.secretFunc()

}func calcLen() -> Int {  

  return objectiveCObject.foo.characters.count

}

上面这段 Swift 代码执行到calcLen()时会崩溃,原因是fooinit()中已经被设成了 nil,而foo在 Swift 中是 foo!。也就是说,因为 Swift 对 OC 变量的强转,导致了程序的崩溃。这是一个很容易忽略的问题,因为强转的时候,Xcode 不会给出任何的警告、报错或是提醒。而我们作为开发者,很容易忽略这样的错误,导致 runtime 的时候直接崩溃。

针对这种情况,我们来讨论下面三个问题。

  • 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 在语言设计思路上的差异。

其实nullablenonnull是 Swift 出来之后才引入 OC 的。所以一开始,OC 中的变量默认都是nullable,转变到 Swift 中,应该就是?。但是这样转化代价太大,我们所有变量都要在 Swift 中用if else或者guard来解包。所以为了写起来方便,Swift 干脆直接强转,故而现在这个机制也是一个历史遗留问题。

  • Q: Swift 如此这般导致混编 App 崩溃,没有提示的情况下程序员必须细细检查 nil 导致的 bug,这样设计强制解包的代价是否有点大?

这个 bug 在混编 App 中很容易出现,没有警告确实带来很大困扰。实际上早就在苹果的开发者论坛上被提出,Swift 组自己也开了个  要修,可惜最后不了了之。Github 上有人开发出了来解决这个问题。

我个人觉得这个问题苹果不重视的原因在于 Swift 和 OC 混编只是一个暂时的局面。Swift 取代 OC 是一个时间问题,所以解决混编中的问题都显得没有多大意义,在苹果内部也一直是低优先级。毕竟现在所有精力应该放在 Swift 上,随着时间的推移和 OC 的淡出,这个问题也将微不足道。

参考链接:


 从强制解包看 Swift 的设计 从强制解包看 Swift 的设计 从强制解包看 Swift 的设计 从强制解包看 Swift 的设计 从强制解包看 Swift 的设计 从强制解包看 Swift 的设计 从强制解包看 Swift 的设计

从强制解包看 Swift 的设计 从强制解包看 Swift 的设计 从强制解包看 Swift 的设计 从强制解包看 Swift 的设计 从强制解包看 Swift 的设计 从强制解包看 Swift 的设计 从强制解包看 Swift 的设计 从强制解包看 Swift 的设计

从强制解包看 Swift 的设计

有意思啊

还要错过关注我?

长按二维码

关注的不仅仅是技术



以上是关于从强制解包看 Swift 的设计的主要内容,如果未能解决你的问题,请参考以下文章

为啥 Swift 2 偏爱强制解包而不是可选项?

Swift中的Optional类型 (可选类型)与强制解包 ? !

尽管强制解包 IBOutlets,类没有初始化器?

Swift基础小结_2

swift项目第四天:动态加载控制器

Swift ?和!