运行环境:Xcode 11.1 Swift5.0
最近参与的一个项目需要从Objective-C(以下简称OC)转到Swift,期间遇到了一些坑,于是有了这篇总结性的文档。如果你也有将OC项目Swift化的需求,可以作为参考。
OC转Swift有一个大前提就是你要对Swift有一定的了解,熟悉Swift语法,最好是完整看过一遍官方的Language Guide。
转换的过程分自动化和手动转译,鉴于自动化工具的识别率不能让人满意,大部分情况都是需要手动转换的。
自动化工具
有一个比较好的自动化工具Swiftify,可以将OC文件甚至OC工程整个转成Swift,号称准确率能达到90%。我试用了一些免费版中的功能,但感觉效果并不理想,因为没有使用过付费版,所以也不好评价它就是不好。
Swiftify还有一个Xcode的插件Swiftify for Xcode,可以实现对选中代码和单文件的转化。这个插件还挺不错,对纯系统代码转化还算精确,但部分代码还存在一些识别问题,需要手动再修改。
手动Swift化
桥接文件
如果你是在项目中首次使用Swift代码,在添加Swift文件时,Xcode会提示你添加一个.h
的桥接文件。如果不小心点了不添加还可以手动导入,就是自己手动生成一个.h
文件,然后在Build Settings > Swift Compiler - General > Objective-C Bridging Header
中填入该.h
文件的路径。
这个桥接文件的作用就是供Swift代码引用OC代码,或者OC的三方库。
#import "Utility.h"
#import <Masonry/Masonry.h>
复制代码
在Bridging Header
的下面还有一个配置项是Objective-C Generated Interface Header Name
,对应的值是ProjectName-Swift.h
。这是由Xcode自动生成的一个隐藏头文件,每次Build的过程会将Swift代码中声明为外接调用的部分转成OC代码,OC部分的文件会类似pch
一样全局引用这个头文件。因为是Build过程中生成的,所以只有.m
文件中可以直接引用,对于在.h
文件中的引用下文有介绍。
Appdelegate(程序入口)
Swift中没有main.m
文件,取而代之的是@UIApplicationMain
命令,该命令等效于原有的执行main.m
。所以我们可以把main.m
文件进行移除。
系统API
对于UIKit
框架中的大部分代码转换可以直接查看系统API文档进行转换,这里就不过多介绍。
property(属性)
Swift没有property
,也没有copy
,nonatomic
等属性修饰词,只有表示属性是否可变的let
和var
。
注意点一 OC中一个类分.h
和.m
两个文件,分别表示用于暴露给外接的方法,变量和仅供内部使用的方法变量。迁移到Swift时,应该将.m
中的property标为private
,即外接无法直接访问,对于.h
中的property不做处理,取默认的internal
,即同模块可访问。
对于函数的迁移也是相同的。
注意点二 有一种特殊情况是在OC项目中,某些属性在内部(.m
)可变,外部(.h
)只读。这种情况可以这么处理:
private(set) var value: String
复制代码
就是只对value
的set
方法就行private
标记。
注意点三 Swift中针对空类型有个专门的符号?
,对应OC中的nil
。OC中没有这个符号,但是可以通过在nullable
和nonnull
表示该种属性,方法参数或者返回值是否可以空。
如果OC中没有声明一个属性是否可以为空,那就去默认值nonnull
。
如果我们想让一个类的所有属性,函数返回值都是nonnull
,除了手动一个个添加之外还有一个宏命令。
NS_ASSUME_NONNULL_BEGIN
/* code */
NS_ASSUME_NONNULL_END
复制代码
enum(枚举)
OC代码:
typedef NS_ENUM(NSInteger, PlayerState) {
PlayerStateNone = 0,
PlayerStatePlaying,
PlayerStatePause,
PlayerStateBuffer,
PlayerStateFailed,
};
typedef NS_OPTIONS(NSUInteger, XXViewAnimationOptions) {
XXViewAnimationOptionNone = 1 << 0,
XXViewAnimationOptionSelcted1 = 1 << 1,
XXViewAnimationOptionSelcted2 = 1 << 2,
}
复制代码
Swift代码
enum PlayerState: