PPPan's 平凡之路

做一个互联网内容的贡献者

PPPan's 平凡之路 一个技术博客覆盖范围包括 iOS Objecticve-C Swift Xcode 等


Objective-C与Swift混编实践

###前言

由于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
@objc(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完善的文档!

Newer Post

电池电量与低电量模式

优步最近公布的数据显示,当电池快没电的时候,人们更愿意接受溢价。同时,优步也表明他们并没有利用电量相关的数据来设置溢价规则。换句话说,我们在开发App的时候,可以通过电池电量来”提升用户体验”。 电池状态与电量UIDevice 中有三个电池相关的属性: 123@property(nonatomic, …

继续阅读
Older Post

Storyboard的爱与恨(下)

Scene的转场如我们所料,Storyboard也可以通过可视化的操作来实现Scene的转场。 故事板的转场有两种,可以分为手动触发和自动触发。自动触发完全由Storyboard实现,而手动触发则需要配合代码。前者简单易用,后者适用于配合业务逻辑,进行不同转场的触发。自动触发的转场非常简单,我们只需 …

继续阅读