这是一篇翻译自Use Your Loaf的文章,介绍了一些 Xcode6 新引入的重构工具
点击这里查看原文
从很久以前开始 Xcode 就引入了一些用于重构 Objective-C 代码的工具(Edit > Refactor > Convert to Modern Objective-C Syntax…),这些工具旨在使用更多的现代化语言特性。我觉得这很有意思我们可以从中看出苹果所提倡的好的做法,即使你不相信 Xcode 的自动重构可能会提高代码质量。
Xcode 6 不但带来了一些新的工具,而且增加了更好的灵活性,你可以选择进行怎样的转化
不幸的是仅凭名称很难弄清每个过程具体的内容。在苹果的文档和WWDC 2014 Session 417 LLVM 新特性的一个 Demo 中有一些更详细的说明。这篇文章就是我对每个转化记录的总结。
@Property 语法
property 的新语法已经不是什么新鲜事。Xcode 6 在这个基础上引入了两个新的转化进行 property 生成和一个可以控制生成的 property 原子性的选项。
- 从 getter 方法中生成 readonly property (默认打开)
- 从 setter 方法中生成 readwrite property (默认打开)
这个转化通过寻找可能是 getter 和 setter 的方法来判断是否缺少对应的 property 声明。例如,一个类中如果有一下两个方法但是没有对应的 property 声明
|
|
Xcode 会自动生成 property 加到头文件中去
|
|
property 的声明可以使这两个方法的用意更明确,并且使编译器可以自动生成对应的读写方法。注意 Xcode 并不会为你去掉原有的方法,如果在原来的方法中有自定义的行为这样做会很危险。这个转化也会在遇到一些不是 getter 或 setter 的方法时建议采用 property,这使得这个转化不怎么实用。
- 生成 property 的原子性 (默认 NS_NONATOMIC_IOSONLY)
在生成 property 时这个选项让你选择生成的 property 是 atomic, nonatomic 还是使用宏命令 NS_NONATOMIC_IOSONLY
。这个宏命令的作用是在 iOS 上定义为 nonatomic 在 OS X 上什么都没有。对于要在两个平台之间重用的代码这是个好的选择。如果在用于 iOS 继续使用 nonatomic。
|
|
指定默认初始化方法
指定默认初始化方法会在一个类中寻找默认方法并用NS_DESIGNATED_INITIALIZER
标记它。为了理解为什么这个转化很有用,我们需要回顾一下在 Objective-C 中对象的初始化过程。创建一个 Objective-C 对象有两个步骤分配内存空间和初始化,通常在一行中这么写:
|
|
初始化方法中会进行实例变量的赋值等其他对象初始化过程。一个类可以有许多以init
前缀的初始化方法。例如,一个类有一个必须赋值的实例变量name
那么包含 name 的初始化方法是这样的
|
|
在这个例子中,init
方法是一个用默认值调用默认初始化方法initWithName:
的快捷初始化方法。默认初始化方法通过向父类发送初始化消息来确保对象已经完全初始化。这个实现过程对于要进行继承时十分重要。默认初始化方法的规则是:
- 默认初始化方法必须调用父类的默认初始化方法。当父类是 NSObject 就是 [super init]
- 任何快捷初始化方法必须调用另一个初始化方法,最终必须调用默认初始化方法。
- 一个类的默认初始化方法必须调用所有父类的默认初始化方法
一直以来我们没有办法使编译器和类的使用者知道那个一个方法是默认初始化方法,除了写明在注释中。为了改变这种现状 Clang 现在添加了 objc_designated_initializer 属性。在 iOS8 中 NSObjCRuntime.h 定义了宏命令 NS_DESIGNATED_INITIALIZER
来简化添加这个属性的方法
|
|
所有在上面的例子中我们会这样修改
|
|
如果一个快捷初始化方法没有调用默认初始化方法你会被警告。我见过一些人提交问题指出一些 UIKit 类苹果并没有标记默认初始化方法,所以想往常一样你需要进行测试提交意外情况的错误报告。
标记方法返回值为Instancetype
这个转变会将包含”alloc”,”init”和”new”的方法返回值 id
替换成 instancetype
。对于一些类工厂方法你可能需要自己手动替换。在这篇(NSHipster文章)[http://nshipster.com/instancetype/]中你会了解采用 instancetype 可以带来的类型安全。
检测协议的一致性
这个默认关闭的转化会添加可能漏掉的协议声明。例如在一个没有声明任何协议的 viewController 中:
|
|
如果这个类实现了 table view data source 两个必须实现的方法 -tableView:numberOfRowsInSection:
和 -tableView:cellForRowAtIndexPath:
那么这个类的头文件会被修改为:
|
|
就我所知协议一致性是通过检查是否实现所有必须实现的方法来进行的。对于 UITableViewDelegate 种只有可选方法的协议来说就无法起作用。
Objective-C 语法糖
- ObjC 语法糖
- ObjC 下标
这两个转变在 Xcode 5 中已经有了,所以我只是提供一个简单的例子
|
|
重构为使用(语法糖和下表)[http://clang.llvm.org/docs/ObjectiveCLiterals.html]的格式是:
|
|
枚举型
- 使用 NS_ENUM/NS_OPTIONS 宏命令
更现代的宏命令 NS_ENUM 和 NS_OPTIONS 可以更简捷的创建指定类型的大小的枚举型。例如:
|
|
重构为
|
|
同理
|
|
重构为
|
|
其他
- 添加属性注释
我没能让这个转化起作用。说明文字介绍了这个方法会为 property 和方法添加属性说明但是我没有发现有什么结果,如果你有请留言…