###前言
由于Swift的语法趋于稳定,目前本人负责的项目也已经在小范围地引入Swift。关于混编过程中遇到的各种思考与选择,胡波的这篇文章已经阐述得得比较全面了,看完文章回头看自己在混编过程中的各种选择,与胡先生的看法是一致的。在此梳理一下一些值得注意的小细节。
Optional
基于Objective-C的工程一般都是将JSON转为Model,把数据以Model的形式在对象之间传递。当服务器传过来的值为空或者类型错误时,大部分解析框架都会将相对应的值置为nil。我们知道在Objective-C的世界里,向nil发送消息是不会引发任何错误的。
Objective-C中没有任何问题的属性:
1 | @property (nonatomic, strong) NSString *GroupName; |
到了Swift的世界里,变成了:
没错,隐式解析的存在,导致了容错能力的下降。只要服务端回传的参数中有一个空字段,必然引发Crash。这简直是自寻死路。
因此我们需要在Objective-C中,将属性标上nullable
,这样Swift中该属性变成了optional,然后采用if let
或者guard let
来安全地拆包。
即使业务上决定了某些字段绝对不可能为null,也仍然要将其设为nullable。校验任何外来输入是编程时的基本准则之一。
想必大家也想到了,这样一来,在混编的过程中,if let充斥着Swift的代码,虽然保证了安全,但一定程度上降低了开发效率。个人还在不断摸索解决这个问题的方法。
Selector
在Objective-C的世界里,@selector
是我们的老朋友了。但到了Swift中我们不得不这样来用Selector
:
1 | self.addTarget(self, action: "resignFristResponder", forControlEvents: .EditingDidEndOnExit) |
如果不仔细看,一定不会发现上面的代码中我错将resignFirstResponder
写成了resignFristResponder
。这在编译时不会出现任何提醒,但到了运行时会引起崩溃。
幸运的是,Swift2.2用#selector
关键字替代了字符串反射。此时拼写错误可以被编译期正确地纠正了–因为编译期会检查到方法不存在,并报一个error。
你可以这样写一个不带参数的selector:
1 | #selector(resignFirstResponder) |
如果是带参数的selector,则会稍微特别一点:
1 | #selector(textFieldDidChange(_:) |
采用下划线来忽略参数名,但记得保留分号。
update:2016年06月22日
在Swift 3中,由于第一个参数也必须指定参数名,因此,selector的语法稍稍有一点点改变:
1 | #selector(textFieldDidChange(textField:) |
有一点需要注意一下,Selector只支持反射Objective-C的方法,如果想要让#selector能正确识别Swift的方法,需要在方法前面加上@objc
关键字
命名
Swift得益于Module而避免了类的命名冲突。在用Swift编写代码的时候,应当遵循Apple官方的命名规范。例如类命名不需要在类名前加前缀。如果类也需要在Objective-C中被调用,可以用@objc关键字来为Objective-C生成对应的类名。
1 | PSLimitedTextField) public class LimitedTextField: UITextField { ( |
同时API的设计也建议按照官方的指导原则来。我从开始写Objective-C时就尽量模仿Apple的命名方式,后来在Objective-C到Swift的自动桥接上尝到了甜头。比如说:
1 | - (instancetype)initWithName:(NSString *)name; |
被自动桥接成了
1 | init(name: String) |
工厂方法
如果仔细对比Objective-C和Swift的接口,你会发现有的类的工厂方法消失了,有的类的还在。这同样是由于命名的问题所导致的。Apple将那些同类名一致的工厂方法桥接成了init方法,将一些默认单词(default,standard,shared etc.)开头的工厂方法保留了下来。
如:[NSDate date]
变成了NSDate()
.
但[NSUserDefaults standardUserDefaults]
,保留了下来,变成了NSUserDefaults.standardUserDefaults()
PSNumberPad就因为工厂方法的命名问题,没能自动桥接成Swift的方法。因此,在混编过程中,如果Objective-C的组件有可能被Swift调用的,需要妥善设计接口以便自动桥接。一个最佳实践是,多采用convince initialzer替代工厂方法。
参考资料
在混编的过程中,大部分问题你可以在这里找到想要的答案。再一次感谢Apple完善的文档!