零. 前言
Swift 版本升级嘛,大家应该都很熟练了,菜单 -> Edit -> Convert -> To Current Swift Syntax…,然后巴拉巴拉一顿操作。emmmn,抱歉,编译过了也不一定能正常使用。
这次 Swift 3 到 Swift 4 的更新和之前的大版本更新相比,已经平滑了很多,相较之前的动辄几百上千个 error,现在用 Xcode 进行 Convert 之后基本上只需要进行少量人工修正即可,不过仍然有一些点需要注意,本文将会对一些常见的坑或者注意点以及解决方法进行讨论。
本文以 EFCountingLabel 的 1.0.3 版本和 Xcode 9.0 为例,主要关于原有的 Swift 3 的 CocoaPods 库到 Swift 4 的升级,仍处于 Swift 2 阶段的同学可暂时忽略本文。
一. 升级流程
1. 查看当前版本
首先用 Xcode 打开工程,看一下当前工程设置的 Swift 版本,如果过低的话可能无法直接 Convert,选中需要转换的 target 搜索 swift_ver
即可,如图所示:
这里 EFCountingLabel 的 Swift 版本为 3.2,如果是 2.x 的话需要自己想办法先转换成 Swift 3.x…
2. Xcode 代码转换
接下来,就是利用 Xcode 实现代码转换了,菜单 -> Edit -> Convert -> To Current Swift Syntax…,然后选中需要转换的 target,点击 Next
按钮即可:
3. 选择转换模式
然后会出现一个转换模式选项,有 Minimize Inference(recommended)
和 Match Swift 3 Behavior
两个选择,苹果推荐的是第一个选项:
苹果官方文档对这两个选项的描述如下,大意是:如果选第一个选项,会仅在必要的时候为方法或属性添加 @objc
标志,不过大部分工作需要用户(也就是你)手动完成,好处是能减少最终生成的二进制文件的大小;如果选择第二个选项,则会按 Swift 3 的方式给所有的地方直接添加 @objc
标志(关于 @objc
标志的介绍大家可以参考 Swift 翻译组的这篇文章),缺点就是不会对生成的二进制文件大小进行优化(也就是跟 Swift 3 一样):
这里我们分几种情况:
- 如果你的 Swift 库不打算支持 OC 调用的话,选
Minimize Inference(recommended)
,检查并且保存自动转换结果即可,然后可以直接跳到下一小节,请忽略下面这一大段; - 如果你的 Swift 库打算支持 OC 调用,但是开发时间紧迫暂时没时间仔细设置
@objc
标志或者对这一点二进制文件体积的缩减并不是十分在意的话,选Match Swift 3 Behavior
,检查并且保存自动转换结果即可,然后可以直接跳到下一小节,请忽略下面这一大段; - 如果你的 Swift 库打算支持 OC 调用,并且打算用推荐的方式进行优化的话,选
Minimize Inference(recommended)
,保存更改,然后按下面的操作去做:
1. 编译工程;
2. 修正那些提示你需要添加 @objc 标志的警告(请务必修正,不然即使编译能过运行时也可能会出问题);
3. 修正 Xcode 提示的不需要添加 @objc 标志的代码,持续构建和测试你的代码,直到没有任何警告出现;
4. 打开工程设置;
5. 选中 target,搜索 `@objc` 找到 `Swift 3 @objc Inference` 选项,设为 `Default`。
唔,以上这段大概是原文翻译过来的了,官方文档原文如图所示:
需要注意的是,因为我们这里针对的并不是完整的 iOS 项目,而是 CocoaPods 库,如果你的 OC Demo 没有调用库中需要暴露的功能(或者干脆没有 OC Demo),辣么编译器可能完全不会给你任何提示而是直接通过编译了,直到你某一天在一个 OC 工程中引入这个库才会发现并不能调用到某些方法或获取某些属性。
所以其实麻烦之处在于,编译器并不会给你任何提示,因为编译器也不知道哪些类 / 属性 / 方法需要暴露,哪些需要被优化掉,需要开发人员自己决定并手动添加对应的 @objc
标志,总结起来的话有以下几点:
- 需要在 OC 中调用一个 Swift 4 的类,需要让这个类继承 NSObject 并且在这个类前加上 @objc 标志;
- 需要在 OC 中调用一个 Swift 4 类的方法,需要在方法前加上 @objc 标志(这里有一个坑,如果是普通的函数调用还好,至少编译器会报错,如果是用
#selector
的方式调用的话,能过编译并且在运行时直接找不到对应方法而闪退,建议升完 Swift 4 检查一下所有的 #selector 调用); - 需要在 OC 中访问一个 Swift 4 类的某个属性,需要在属性前加上 @objc 标志(同上,如果是普通属性访问的话编译器会报错,但是 KVC 的话会在运行时找不到属性而崩溃,记得检查…);
- 需要在 OC 中访问一个 Swift 4 类的扩展,只要在扩展前加上 @objc 标志,该扩展的属性和方法就都能被调用了。
4. 更新 Xcode 设置
- 如下图所示,根据 Xcode 提示将工程设置进行更新,点击 Warning 后单击
Perform Changes
按钮即可;
- 检查设置,将所有 target 的
Swift 3 @objc Inference
设置(如果有的话)改为Default
,之前改过的话就不用改了; - 搜索
swift_ver
,可以看到当前的Swift Language Version
已经是Swift 4
了。
剩下少量方法名变动之类的更新大家可以根据提示自行修改,到这里基本就完成了升级过程,不过先别急,接下来我们看注意事项。
二. 注意事项
以下情况必须要给对应的属性或方法添加 @objc
标志(当然,他们所在的类肯定也需要添加 @objc
标志),不管是通过 OC 还是 Swift 调用:
- 使用
@selector()
或#selector()
方式调用的函数; - 使用 KVC 进行访问的属性;
- 使用 IBOutlet 或者 IBAction 和 StoryBoard 绑定的函数或属性。
这些有部分在官方文档中也有提及:
三. 一些问题
- 同一工程的 Pods 库是否可以既有 Swift 3 的也有 Swift 4 的?
Swift 的版本控制粒度在 framework 层面,也就是说同一个工程中不同的 framework 可以是按不同版本的 Swift 进行编译的,所以并不需要等待项目依赖的所有 Pods 库都支持 Swift 4 后再更新,完全可以将已经升级 Swift 4 的库先用起来。
Swift 3 @objc Inference
选项是干啥的?
在 Swift 4 之前,编译器对 Objective-C 自动提供了一些 Swift 声明。例如,编译器会为 NSObject 子类的所有方法创建 Objective-C 入口点,该机制称为 @objc 推断(@objc Inference)。
在 Swift 4 中,这种自动的 @objc 推断已被废弃,因为生成所有这些 Objective-C 入口点有代价,会增大最终的二进制文件体积。当 Swift 3 @objc Inference
设置为 On
时,它会按照 Swift 4 之前的模式运行,不进行优化,也就是隐式为我们编写的所有 Swift 代码提供 OC 入口。
但是,当设置为 On
时 Xcode 会报一个警告,建议修复这个警告,并将设置切换到 Default
。新的 Swift 项目的默认为“Default”。可以理解为该项设置为 On
时和上文代码转换时选择 Match Swift 3 Behavior
选项效果类似。
四. 没了
升级完请务必跑一遍整体测试流程,暗坑无数,以防万一,祝大家线上稳定。
本文链接:http://www.jianshu.com/p/0cf93a3e475c
如文中无特殊说明,本站均使用以下协议保护:署名-非商业性使用-禁止演绎
如有任何知识产权、版权问题或理论错误,还请指正。
转载请注明原作者及以上信息。