迁移到 Swift 3,这些陷阱在等你

简介: 本文讲的是迁移到 Swift 3,这些陷阱在等你,万岁!Swift 3 发布了,让我们一起来移植项目吧!在这篇文章中,我会你分享我的项目迁移到 Swift 3的经历,那是一个 2 万行的 Swift 项目。如果你对此感到好奇
本文讲的是迁移到 Swift 3,这些陷阱在等你,

万岁!Swift 3 发布了,让我们一起来移植项目吧!在这篇文章中,我会你分享我的项目迁移到 Swift 3的经历,那是一个 2 万行的 Swift 项目。如果你对此感到好奇,这个项目其实是我实现的 Cassowary 线性约束求解算法,该算法最著名之处在于其通常被用于页面的自动布局。但我将它用在了一些完全不同的事情上,我将会在以后的文章中说明。

Swift 移植器

第一步是从 Xcode 中运行 Swift 移植器来对我的项目进行转换。移植器帮助我定位了大部分必须修改的地方,这节省了我很多的工作。而有几件事情我不得不在这之后做出修改,虽然这并不是很麻烦。在我必须重写的功能中,最有趣的是权限变更(新的权限模型默认为使类 public 和方法 open,但我想在大多数情况下限制这一点)和二进制搜索功能,起因是收集索引操作的工作方式的变更。

什么也没有变

根据惯例来看,每次 Swift 发布的新版本,我尝试的第一次编译都会报错。在报错之前编译器日志会输出一个错误列表,之后我会根据列表解决错误,然后代码就可以正常运行了。

我必须修复那些未被移植器捕捉到的,涉及两个语言之间变化所造成的编译错误,在下面两节我会高亮标注这些代码。

新的 Range

第一类必须解决的错误源于新的 Range 结构所带来的语义变更。现在 Swift 3 的 ranges 是由 4 个不同的结构体来重新代表,可数/不可数的范围和开放式/封闭式的范围。而在 Swift 2 中,开放式和封闭式范围使用相同的结构体,所以如果你有一些代码同时使用了这两种范围,那么你需要做一些修改工作。

下面是一个有效的 Swift 2 例子:

func doSomething(with range: Range) {
    for number in range {
        ...
    }
}

doSomething(with: 0..<10) 
doSomething(with:="" 0...10)="" 

在 Swift 2 中,上面的代码在半开放式和封闭式的可计数范围是生效的。移植器没有转换该结构名称,因此在项目移植后这部分代码不生效。在 Swift 3 中, Range 表示半开放不可计数式范围。由于不可计数式范围不支持迭代,所以我们必须改变这一点,而如果我们使它可以在半开放式和封闭式范围都能生效,这将会非常棒。解决方案是通过将输入转换为半开放可计数式范围或使用泛型使它在两种范围下都生效。事实上,这是利用了可计数式范围来实现 Sequence 协议。

这是一段可运行的 Swift 3 版本代码:

func doSomething(for range: IterableRange) 
    where IterableRange: Sequence, IterableRange.Iterator.Element == Int {
    for number in range {
        ...
    }
}

doSomething(with: 0..<10) 
doSomething(with:="" 0...10)="" 

元组转换

另一类编译器报错的原因是元组转换。下面是一段有效的 Swift 2 代码:

typealias Tuple = (foo: Int, bar: Int)

let dict: [Int: Int] = [1: 100, 2: 200]

for tuple: Tuple in dict {
    print("foo is \(tuple.foo) and bar is \(tuple.bar)")
}

移植器保留了这段代码的原貌,可编译器会报 for 循环的类型强制转换为 Tuple 的错误。在使用(key: Int, value: Int)这个元素类型来遍历上面这个字典的时候,Swift 2 环境下完全可以直接把它分配另给一个拥有相同成员类型但是不同成员名的变量。现在可好,再也不支持这个特性了!

虽然我认为严格的类型控制在通常情况下是很好的,这意味着现在我们需要将元组显式转换为目标类型。我们可以通过使用以下 语句来替换循环代码,使代码重新生效运行:

for tuple in dict {
    let tuple: Tuple = (foo: tuple.key, bar: tuple.value)
    print("foo is \(tuple.foo) and bar is \(tuple.bar)")
}

当然,这是一个特别修正过的例子,但是如果你要传递这个元组的值或者你想通过使用基于语义的有效的名称,而不是键/值对的方式,来使得相关的字典使代码更容易理解,那就最好了。

PaintCode 与 Core Graphics

其他类值得一提的错误有 Core Graphics。Swift 3 引入了 Core Foundation-style 对象调用机制,也就是说现在你可以将其当做Swift对象来使用,而不是一组 C 函数。这可以让你的代码很整洁且保持可读性。这一新特性最常见于 Core Graphics 调用。移植器会转换大多数这些调用,但一些较少使用的函数(例如:Arc、Drawing)则不会被转换,所以你必须手动完成这部分的转换工作。

在我的项目中,我使用了大量的 PaintCode。而 PaintCode 的代码生成是出了名的不完全支持最新的 Swift 语法(当前版本仍然会产生对 Swift 2.3 的警告,即使它是一个需要解决的微不足道的问题)我真害怕我的图形代码可能无法正常地转换。幸运地的是上帝眷顾了我,因为移植后的代码并没有出现额外的问题。你可能还是想把代码的可见度从 open 变为 internal,尽管这能从编译器优化技术中受益更多。 (我有一个脚本,已经可以通过一些正则方式解决这个问题)

性能

总的来说,我注意到的是,在移植后我的项目在编译时间上没有显著变化。我的基准单元测试显示,在重度使用 dictionary 的代码中性能有些下降,除此之外没有其他显著变化。我的约束求解器仍然可以快速的生效。:)

最后的想法

总体而言,移植到 Swift 3 还是很容易的。移植器帮助我解决了过程中的大部分变更,而剩下的那部分也很容易修复。如果你对 Swift 还有点陌生 ,那我和你的情况可能会不同,所以你的项目迁移过程也会和我所描述的有所差异。

最后提一个非常有用的小建议:请确保你的项目中,在算法部分有足够多的的单元测试(这从不是一个坏主意!)这样你就可以验证在移植过程中是否有引入语义变化,而如果引入了变化,你也可以找到他们!





原文发布时间为:2016年10月09日

本文来自云栖社区合作伙伴掘金,了解相关信息可以关注掘金网站。
目录
相关文章
|
6月前
|
安全 编译器 Swift
IOS开发基础知识: 对比 Swift 和 Objective-C 的优缺点。
IOS开发基础知识: 对比 Swift 和 Objective-C 的优缺点。
394 2
|
4月前
|
Unix 调度 Swift
苹果iOS新手开发之Swift 中获取时间戳有哪些方式?
在Swift中获取时间戳有四种常见方式:1) 使用`Date`对象获取秒级或毫秒级时间戳;2) 通过`CFAbsoluteTimeGetCurrent`获取Core Foundation的秒数,需转换为Unix时间戳;3) 使用`DispatchTime.now()`获取纳秒级精度的调度时间点;4) `ProcessInfo`提供设备启动后的秒数,不表示绝对时间。不同方法适用于不同的精度和场景需求。
142 3
|
16天前
|
Swift iOS开发 UED
如何使用Swift和UIKit在iOS应用中实现自定义按钮动画
本文通过一个具体案例,介绍如何使用Swift和UIKit在iOS应用中实现自定义按钮动画。当用户点击按钮时,按钮将从圆形变为椭圆形,颜色从蓝色渐变到绿色;释放按钮时,动画以相反方式恢复。通过UIView的动画方法和弹簧动画效果,实现平滑自然的过渡。
31 1
|
25天前
|
Swift iOS开发 UED
如何使用Swift和UIKit在iOS应用中实现自定义按钮动画
【10月更文挑战第18天】本文通过一个具体案例,介绍如何使用Swift和UIKit在iOS应用中实现自定义按钮动画。当用户按下按钮时,按钮将从圆形变为椭圆形并从蓝色渐变为绿色;释放按钮时,动画恢复原状。通过UIView的动画方法和弹簧动画效果,实现平滑自然的动画过渡。
47 5
|
3月前
|
存储 移动开发 Swift
使用Swift进行iOS应用开发:探索现代移动开发的魅力
【8月更文挑战第12天】使用Swift进行iOS应用开发,不仅能够享受到Swift语言带来的简洁、快速、安全的编程体验,还能够充分利用iOS平台提供的丰富资源和强大功能。然而,iOS应用开发并非易事,需要开发者具备扎实的编程基础、丰富的实践经验和不断学习的精神。希望本文能够为您的iOS应用开发之旅提供一些有益的参考和帮助。
|
4月前
|
Swift iOS开发 Kotlin
苹果iOS新手开发之Swift中实现类似Kotlin的作用域函数
Swift可通过扩展实现类似Kotlin作用域函数效果。如自定义`let`, `run`, `with`, `apply`, `also`,增强代码可读性和简洁性。虽无直接内置支持,但利用Swift特性可达成相似功能。
71 7
|
4月前
|
调度 Swift Android开发
苹果iOS新手开发之Swift中的并发任务和消息机制
Swift的消息机制类似Android的Handler,实现任务调度有三种方式: 1. **Grand Central Dispatch (GCD)**:使用`DispatchQueue`在主线程或后台线程执行任务。 2. **OperationQueue**:提供高级接口管理`Operation`对象。 3. **RunLoop**:处理事件如输入源、计时器,类似Android的`Looper`和`Handler`。 **示例**: - GCD:在不同线程执行代码块。 - OperationQueue:创建操作并执行。 - RunLoop:用Timer添加到RunLoop中。
99 2
|
4月前
|
安全 编译器 Swift
探索iOS开发:Swift语言的现代魔法
【7月更文挑战第11天】本文深入探讨了Swift编程语言,它如何革新iOS开发领域,以及它为开发者带来的独特优势。我们将从Swift的基础语法出发,通过实际案例分析其性能优化技巧,最后讨论Swift在跨平台开发中的潜力。文章旨在为读者提供一个全面而深入的视角,了解Swift不仅仅是一门语言,更是一种推动创新的力量。
|
6月前
|
设计模式 前端开发 Swift
使用Swift进行iOS应用开发:深入探索与最佳实践
【5月更文挑战第24天】探索Swift在iOS开发中的深度应用与最佳实践。Swift以其简洁语法、类型安全、面向对象、高性能及与Objective-C的互操作性脱颖而出。使用Xcode设置开发环境,学习Swift语法,创建并设计项目,编写业务逻辑,同时进行调试和测试。遵循MVC模式,利用SwiftUI、并发特性,并注重内存管理,持续学习新工具和技术,以实现高质量应用开发。
|
6月前
|
安全 Swift iOS开发
【Swift 开发专栏】Swift 与 UIKit:构建 iOS 应用界面
【4月更文挑战第30天】本文探讨了Swift和UIKit在构建iOS应用界面的关键技术和实践方法。Swift的简洁语法、类型安全和高效编程模型,加上与UIKit的紧密集成,使开发者能便捷地创建用户界面。UIKit提供视图、控制器、布局、动画和事件处理等功能,支持灵活的界面设计。实践中,遵循设计原则,合理组织视图层次,运用布局和动画,以及实现响应式设计,能提升界面质量和用户体验。文章通过登录、列表和详情界面的实际案例展示了Swift与UIKit的结合应用。
294 1