Swift 4.1 迁移指南
Posted 小得写代码
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Swift 4.1 迁移指南相关的知识,希望对你有一定的参考价值。
*代码建议横屏阅读
理论上来讲,升级到一个小版本的语言和SDK的更新,应该是个比较顺滑的过程。然而这次Swift 4.1 / Xcode 9.3 的升级所带来的工作超出了预期。下面分『通过编译』、『通过测试』以及『去除警告』三个步骤来说。
1. 通过编译
相信这次 Swift 4.1 的升级对于有一定代码量并且引用第三方库源码编译的项目来说,需要处理的事情还不少,通过编译就没那么容易,先看下面的代码
aView.y = bounds.height + margin - (102+20+20+36)/2.0
编译居然出错了:
Expression was too complex to be solved in reasonable time; consider breaking up the expression
看到这个错误时,笔者真的笑了又笑、笑出了声。首先这个表达式没那么复杂,居然编译器认为在合理时间中编不过了。但同时,我又表示了充分的理解,因为Swift 4.1之前版本中,对于这种『复杂』的表达式编译的速度是非常慢的。修正方式:拆一个局部变量出来,写成两段。
这个编译错误的出现,意味着 Swift 4.1 中,编译速度得到了监控。我亲测了一下,我们整个项目编译时间缩短了30%以上,之前复杂类型推断的方法需要编译几分钟,这个问题在 Swift 4.1 中得到了修复。
我们来看一下第二个编译不过
func asyncDoSth(_ completion: ((Void) -> Void)?) {
// compile error
completion?()
}
func asyncDoSth(_ completion: (() -> Void)?) {
completion?()
}
我们首先来复习一下,Void
实质上是一个空的tuple,即()
,所以第一个方法如果需要编译过的话,completion?( () )
,可以传入一个空 tuple。对于一般的应用场景来说,其实不太会碰到这个编译不过的问题,因为第二种写法是我们的通常写法。典型的问题场景一般与泛型框架有关。
第三个编译不过不是 Swift 相关了,而是 Objective-C 的 格式化相关的函数报编译错误了,我们使用了 IGListKit 框架,这个框架正好踩中了这个坑,官方修复速度不足够快,那自己上了。问题的本质是 NSInteger 这样的类型在32位和64位机器上的 size 是不一样的,因此一个安全的格式化的方式是统一向上转型,然后用%ld输出。
2. 通过测试
编译过只是第一步,新的 SDK 和 语言的升级也会带来新的问题。这时候经过简单自测后,需要请测试同学帮忙全量回归测试了。不测不知道,一测吓一跳。
第一个P0 Bug,分享跳转回来都挂了!
func application(_ application: UIApplication, open url: URL,
sourceApplication: String?, annotation: Any) -> Bool {
由于一些众所周知的原因,我们还需要 hook 这个已经废弃的方法来实现应用间跳转的流程,可是,分享回来之后,在任何函数被调用之前,居然挂了。
经过一番折腾发现,似乎是 nil 不能被转换成 Any,WTF? 权宜之计,在 annotation 的 Any 之后加一个问号吧。笔者测试了 Xcode 9.4 Beta,发现在 Swift 4.1.1 中这个 Bug 被修复,属于 Swift 标准库的问题。
第二个Bug,隐藏得就更深了,与Swift 内部实现机制有关(同时访问)
protocol Z {
var x: Int {get set}
var y: Int {get set}
}
struct ZZZ : Z {
var x : Int
var y : Int
}
class A {
var p : Z = ZZZ(x: 2, y: 3)
func f() {
p.y = max(p.x, p.y)
}
}
A().f()
Swift 4.1 中会出现以下运行时错误,同时访问了p.y
Simultaneous access to 0x10217f930, but modification requires exclusive access
这个问题 tricky 的地方在于,如果你直接操作类型 ZZZ,得到的是编译时错误;但是如果你通过了protocol一层间接访问,它隐藏成了运行时错误。解决方法:返回值提取中间变量,再赋值。
3. 去除警告
完成以上两步,我们迁移的基本工作已经完成了,有时间的话,应当开展或者提前开始第三步,去除警告。警告不应该累积,以免不得不改的时候改动量太大。我们来看一些典型的:
Sequence.flatMap 返回 Optional 的情况下,应该使用 compactMap
deallocate(capacity:) 参数没用,直接去掉
protocol 中的 weak property 声明中,weak应该去掉
小结
在本文中,我们讨论了Swift 4.1 迁移中的发现:
编译的问题和解决方案
运行时的 Bug 和『特性』,及其解决方案
需要注意去除的警告
编译速度总体提升
历史文章回顾
以上是关于Swift 4.1 迁移指南的主要内容,如果未能解决你的问题,请参考以下文章