Swift 4.1 迁移技巧

作者: 面试官小健 | 来源:发表于2018-05-22 17:45 被阅读74次

    理论上来讲,升级到一个小版本的语言和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 迁移中会碰到的问题:

    • 编译的问题和解决方案
    • Swift 4.1 的 Bug 和『特性』,及其它的解决方案
    • 需要注意去除的警告
    • Swift 4.1 编译速度提升了
    欢迎关注公众号

    Swift 4.1 新特性系列文章

    Swift 4.1 新特性 (1) Conditional Conformance
    Swift 4.1 新特性 (2) Sequence.compactMap
    Swift 4.1 新特性 (3) 合成 Equatable 和 Hashable
    Swift 4.1 新特性 (4) Codable的改进
    Swift 4.1 新特性 (5) 关联类型的递归约束

    相关文章

      网友评论

        本文标题:Swift 4.1 迁移技巧

        本文链接:https://www.haomeiwen.com/subject/unqkjftx.html