Xcode6中进行Objective-C现代化的工具

这是一篇翻译自Use Your Loaf的文章,介绍了一些 Xcode6 新引入的重构工具
点击这里查看原文

从很久以前开始 Xcode 就引入了一些用于重构 Objective-C 代码的工具(Edit > Refactor > Convert to Modern Objective-C Syntax…),这些工具旨在使用更多的现代化语言特性。我觉得这很有意思我们可以从中看出苹果所提倡的好的做法,即使你不相信 Xcode 的自动重构可能会提高代码质量。

Xcode 6 不但带来了一些新的工具,而且增加了更好的灵活性,你可以选择进行怎样的转化

imgimg

不幸的是仅凭名称很难弄清每个过程具体的内容。在苹果的文档WWDC 2014 Session 417 LLVM 新特性的一个 Demo 中有一些更详细的说明。这篇文章就是我对每个转化记录的总结。

@Property 语法

property 的新语法已经不是什么新鲜事。Xcode 6 在这个基础上引入了两个新的转化进行 property 生成和一个可以控制生成的 property 原子性的选项。

  • 从 getter 方法中生成 readonly property (默认打开)
  • 从 setter 方法中生成 readwrite property (默认打开)

这个转化通过寻找可能是 getter 和 setter 的方法来判断是否缺少对应的 property 声明。例如,一个类中如果有一下两个方法但是没有对应的 property 声明

     
     
1
2
     
     
- (NSString *)name;
- (void)setName: (NSString *)newName;

Xcode 会自动生成 property 加到头文件中去

     
     
1
     
     
@property ( nonatomic, copy) NSString *name;

property 的声明可以使这两个方法的用意更明确,并且使编译器可以自动生成对应的读写方法。注意 Xcode 并不会为你去掉原有的方法,如果在原来的方法中有自定义的行为这样做会很危险。这个转化也会在遇到一些不是 getter 或 setter 的方法时建议采用 property,这使得这个转化不怎么实用。

  • 生成 property 的原子性 (默认 NS_NONATOMIC_IOSONLY)

在生成 property 时这个选项让你选择生成的 property 是 atomic, nonatomic 还是使用宏命令 NS_NONATOMIC_IOSONLY。这个宏命令的作用是在 iOS 上定义为 nonatomic 在 OS X 上什么都没有。对于要在两个平台之间重用的代码这是个好的选择。如果在用于 iOS 继续使用 nonatomic。

     
     
1
     
     
@property (NS_NONATOMIC_IOSONLY, readonly, copy) NSDate *dueDate;

指定默认初始化方法

指定默认初始化方法会在一个类中寻找默认方法并用NS_DESIGNATED_INITIALIZER标记它。为了理解为什么这个转化很有用,我们需要回顾一下在 Objective-C 中对象的初始化过程。创建一个 Objective-C 对象有两个步骤分配内存空间和初始化,通常在一行中这么写:

     
     
1
     
     
MyObject * object = [[MyObject alloc] init];

初始化方法中会进行实例变量的赋值等其他对象初始化过程。一个类可以有许多以init前缀的初始化方法。例如,一个类有一个必须赋值的实例变量name那么包含 name 的初始化方法是这样的

     
     
1
2
3
4
5
6
7
8
9
10
11
     
     
- (instancetype) init {
return [ self initWithName:@ "Unknown"];
}
- (instancetype)initWithName:( NSString *)name {
self = [ super init];
if ( self) {
_name = [name copy];
}
return self;
}

在这个例子中,init方法是一个用默认值调用默认初始化方法initWithName:的快捷初始化方法。默认初始化方法通过向父类发送初始化消息来确保对象已经完全初始化。这个实现过程对于要进行继承时十分重要。默认初始化方法的规则是:

  • 默认初始化方法必须调用父类的默认初始化方法。当父类是 NSObject 就是 [super init]
  • 任何快捷初始化方法必须调用另一个初始化方法,最终必须调用默认初始化方法。
  • 一个类的默认初始化方法必须调用所有父类的默认初始化方法

一直以来我们没有办法使编译器和类的使用者知道那个一个方法是默认初始化方法,除了写明在注释中。为了改变这种现状 Clang 现在添加了 objc_designated_initializer 属性。在 iOS8 中 NSObjCRuntime.h 定义了宏命令 NS_DESIGNATED_INITIALIZER 来简化添加这个属性的方法

     
     
1
     
     
#define NS_DESIGNATED_INITIALIZER __attribute__((objc_designated_initializer))

所有在上面的例子中我们会这样修改

     
     
1
2
     
     
- (instancetype)init;
- (instancetype)initWithName: (NSString *)name NS_DESIGNATED_INITIALIZER;

如果一个快捷初始化方法没有调用默认初始化方法你会被警告。我见过一些人提交问题指出一些 UIKit 类苹果并没有标记默认初始化方法,所以想往常一样你需要进行测试提交意外情况的错误报告。

标记方法返回值为Instancetype

这个转变会将包含”alloc”,”init”和”new”的方法返回值 id 替换成 instancetype。对于一些类工厂方法你可能需要自己手动替换。在这篇(NSHipster文章)[http://nshipster.com/instancetype/]中你会了解采用 instancetype 可以带来的类型安全。

检测协议的一致性

这个默认关闭的转化会添加可能漏掉的协议声明。例如在一个没有声明任何协议的 viewController 中:

     
     
1
     
     
@interface UYLViewController : UIViewController

如果这个类实现了 table view data source 两个必须实现的方法 -tableView:numberOfRowsInSection: 和 -tableView:cellForRowAtIndexPath: 那么这个类的头文件会被修改为:

     
     
1
     
     
@interface UYLViewController : UIViewController<UITableViewDataSource>

就我所知协议一致性是通过检查是否实现所有必须实现的方法来进行的。对于 UITableViewDelegate 种只有可选方法的协议来说就无法起作用。

Objective-C 语法糖

  • ObjC 语法糖
  • ObjC 下标

这两个转变在 Xcode 5 中已经有了,所以我只是提供一个简单的例子

     
     
1
2
     
     
NSNumber *magicNumber = [ NSNumber numberWithInteger: 42];
NSDictionary *myDictionary = [ NSDictionary dictionaryWithObject:magicNumber forKey: @"magic"];

重构为使用(语法糖和下表)[http://clang.llvm.org/docs/ObjectiveCLiterals.html]的格式是:

     
     
1
2
     
     
NSNumber *magicNumber = @42;
NSDictionary *myDictionary = @{@"magic": magicNumber};

枚举型

  • 使用 NS_ENUM/NS_OPTIONS 宏命令

更现代的宏命令 NS_ENUM 和 NS_OPTIONS 可以更简捷的创建指定类型的大小的枚举型。例如:

     
     
1
2
3
4
5
6
     
     
enum {
UYLTypeDefault,
UYLTypeSmall,
UYLTypeLarge
};
typedef NSInteger UYLType;

重构为

     
     
1
2
3
4
5
6
     
     
typedef NS_ENUM( NSInteger, UYLType)
{
UYLTypeDefault,
UYLTypeSmall,
UYLTypeLarge
};

同理

     
     
1
2
3
4
5
6
7
8
     
     
enum
{
UYLBitMaskA = 0,
UYLBitMaskB = 1 << 0,
UYLBitMaskC = 1 << 1,
UYLBitMaskD = 1 << 2
};
typedef NSUInteger UYLBitMask;

重构为

     
     
1
2
3
4
5
6
7
     
     
typedef NS_OPTIONS(NSUInteger, UYLBitMask)
{
UYLBitMaskA = 0,
UYLBitMaskB = 1 << 0,
UYLBitMaskC = 1 << 1,
UYLBitMaskD = 1 << 2
};

其他

  • 添加属性注释

我没能让这个转化起作用。说明文字介绍了这个方法会为 property 和方法添加属性说明但是我没有发现有什么结果,如果你有请留言…

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值