使用Uncrustify通过服务格式化OC代码
# 前言
Xcode 是开发 iOS 应用的必备工具,但这个工具的辅助功能相对于 Eclipse 之类的 IDE 来说,还是比较少,在 Eclipse 里只要按 Ctrl+Shift+F
、在 Android Studio 里只要按 Ctrl+alt+O
,就可以将 Java 代码格式化得非常好看。而在 Xcode 里,只有 Re-Indent(Ctrl+I)这一个调整缩进的功能,显然不够用。
# 背景
苹果为了避免类似 Xcode Ghost 事件的再次发生,Xcode8 禁用了插件机制,而开放了另一种形式 Source Editor extensions。在这种机制下,所有插件都没办法在官方版本的 Xcode 上使用了,当然可以使用一些奇淫巧技继续使用插件,但有两点。
- 需要拷贝一个新的 Xcode,占了硬盘好几个G。
- 拷贝出来的新的 Xcode,安装插件后不可打包上传到 AppStore。
所以,通过插件来格式化代码是极其不推荐的,牺牲太大。本文不使用插件来格式化代码,而是使用系统的服务。
# 为什么要进行代码格式化
我们是一个团队,而不是一个人。团队中的每个人如果不按规范进行约束,整合到一起的时候就会乱如泥沙。虽然出了规范,但需要每个人都坚持下去,还是很难。
当然,强迫症患者可能就不需要代码格式化了,因为基本可以保证代码写得都很漂亮,至少对齐,空格,换行都不会有任何问题,然而并不是每个人都是这样。
可能从别的项目或者网上的帖子中复制粘贴过来,跑得起来,就不改了。还有一个我超级反感的“顺手”问题?举个例子:
if(flag == YES) {
// ...
}else {
// ...
}
2
3
4
5
绝大部分人都会觉得奇怪,为什么 if 后面,左边的括号前没有空格,而结尾的括号后面却有空格;同样,为什么 else 前面没有空格,而后面会有空格?
我的一个组员回答:“顺手。”,然后所有项目所有代码都是按这个习惯写的。我真的猜不透你是怎么做到的?不管是自己手敲还是 Xcode 的自动补全,都敲不出这样的?一点都不顺手好不好?也许你是想立个 flag,让我们 review 代码的时候一眼就看出是你写的?呵呵。
当然这不是针对某个人,只是举一个例子而已。像这样有可能两边都没有空格,有可能两边都有空格,也有可能左边有而右边没有,更有可能左边没有而右边有,空格也有可能有多个,什么乱七八糟的。
这样就非常需要进行格式化,大家就按一个格式,很多个人习惯,党派之争,随便你爱怎么敲怎么敲,怎么顺手就怎么来,只有一个要求,敲完进行格式化,否则 Merge Request 打回去重改。
当然格式化还有很多好处,至少看起来是真的舒服,eg.
格式化前:
#define kFont [UIFont systemFontOfSize:22]
#define kOtherFont [UIFont systemFontOfSize:15]
#define kAltLabel @"123"
#define kReturnLabel @"Return"
#define kSpaceLabel @"空格"
@interface DKCustomKeyboard()
@property (nonatomic,strong, readwrite) id<UITextInput> textView;
@property(nonatomic, assign,getter=isShifted) BOOL shifted;
@property(nonatomic,strong) NSArray *charArray;
@property(nonatomic,strong) NSArray *charShiftArray;
@property (nonatomic,strong) NSArray *charAltArray;
@end
2
3
4
5
6
7
8
9
10
11
12
13
格式化后:
#define kFont [UIFont systemFontOfSize:22]
#define kOtherFont [UIFont systemFontOfSize:15]
#define kAltLabel @"123"
#define kReturnLabel @"Return"
#define kSpaceLabel @"空格"
@interface DKCustomKeyboard ()
@property (nonatomic, strong, readwrite) id <UITextInput> textView;
@property (nonatomic, assign, getter = isShifted) BOOL shifted;
@property (nonatomic, strong) NSArray *charArray;
@property (nonatomic, strong) NSArray *charShiftArray;
@property (nonatomic, strong) NSArray *charAltArray;
@end
2
3
4
5
6
7
8
9
10
11
12
13
我就问一下会有人觉得格式化后不好看吗?如果有,我都装做没听见。
# Uncrustify
如果想要完备的代码格式化,需要借助 Uncrustify 这样的工具。我们配置一个针对文本的服务,再加上快捷键,就可以在 Xcode 里间接实现和 Eclipse 同样效果的代码格式化功能了。
# 安装
首先安装 Uncrustify 工具。安装的方法很多,推荐用 Homebrew 来安装。
如果机器上没有 Homebrew,先安装:
$ /usr/bin/ruby -e "$(curl -fksSL http://u.aodaren.com/homebrew)"
安装好 Homebrew 后,安装 Uncrustify:
$ brew install uncrustify
# 配置
下载已经配置好的 workflow 包,复制到~/Library/Services/
,并解压:
$ mv Uncrustify-Objective-C.workflow.tar.gz ~/Library/Services/
$ cd ~/Library/Services/
$ tar zxvf Uncrustify-Objective-C.workflow.tar.gz
2
3
下载针对 Objective-C 语言的 Uncrustify 配置文件 uncrustify_obj_c.cfg,移动到~/
目录,并重命名为.uncrustify_obj_c.cfg
,注意是隐藏文件,文件名前面有个.
$ mv uncrustify_obj_c.cfg ~/.uncrustify_obj_c.cfg
# 使用
如果上面已经顺利完成,那么现在到 Xcode 里选中一段代码,点右键,在 Services 里面应该已经有一个 Uncrustify Objective-C
服务了。选择后,即可将代码格式化。
当然,如果每次都点鼠标进行格式化,显然太麻烦,需要配置快捷键。
# 快捷键
点击桌面,再点击屏幕左上角的 Finder
,选择 服务
- 服务偏好设置
,在 文本
这一组的最后,找到刚添加的服务 Uncrustify Objective-C
,给这个服务加上快捷键。
注意不要和 Xcode 里其它快捷键冲突,推荐设置为:Command + Option + O
。
# 后话
这样以后在 Xcode 里,在一个类文件中写完代码,先用 Command + A
全选代码,再用 Command + Option + O
来格式化代码。整个效果和 Eclipse 基本上差不多,只不过多了一步全选代码的操作。
另外有个想法,想做一个脚本,在 pre-commit 这个 hook 中调用,遍历所有的代码文件进行格式化,这样就不需要每次写完都进行一次手动的格式化操作了,而是提交之前自动格式化。(当然这也得进行一些约束,比如代码文件的存放位置等,对一些 xib 等资源文件的格式化就没有必要了,后面再说吧。)