yohunl's iOS blog

时间顺流而下,生活逆水行舟

用来记录平时的一些心得!


OC代码规范的spacecommander使用

引子

大到每个公司,小到每个人,都有自己的编程习惯,好的编程习惯的保留,得到大家的认同,也就成了规范,目前大体上有几个派系的规范,首当其冲的就是Google的规范.

我们平时在Xcode中的格式规范使用的是苹果的规范Apple's Guide,不过官方的规范,定义的比较模糊,很多细节方面并没有做出说明,所以很多大公司都在Apple的规范的基础上细化了相关的规范,最典型的当然是Google了.Google的OC规范的说明在Google's OC Guide,当然了,还有其它一些知名公司也出了自己的规范指导文档,例如Github's Guide,还有在整个OC业界最知名的培训机构raywenderlich(他们出的iOS by tutorials 6,7,8,9,10系列,绝对的经典,是提升之路上必备的经典书籍,没有之一)的规范Raywenderlich's Guide,当然了,还有markeissler.

以上的几个公司的规范指导,都是非常优秀的,遵循任何一个,对于我们代码的提升都是很有帮助的,但是,这里会有个小小的问题,就是,如果基于自己写代码时候去注意是否符合规范,很容易遗漏,或者是不经意的很多部分就不符合规范了,这时候怎么办?

我们Xcode编译使用的编译器早已经换成开源的LLVM了,LLVM机构给出来一个源码的clang格式工具clang-format,LLVM官方使用的是自己的git服务来存放代码的,有点不方便,有人在github上面建立了其镜像llvm-mirror/clang-tools-extra,如果对此工具感兴趣,可以去读读它的源码.

clang-format是一个命令行的工具,你可以通过源码编译来得到这个工具,也可以用brew来安装(brew install clang-format),安装完了就可以使用了,clang-format的基础用法在ClangFormat基本用法,其内置了四种风格-LLVM, Google, Chromium, Mozilla, WebKit,当然,其也提供了常见的风格配置来支持你的自定义风格(这种自定义风格是有限的,只是在其已有的基础上改变某些配置而已,例如,空格是2个字符还是4个字符等等).

大体上如下这样使用:

// 以Google代码风格格式化NHMineInfo.m,结果输出在终端中
clang-format NHMineInfo.m -style=Google  
// 以Google代码风格格式化NHMineInfo.m,结果直接写入文件中
clang-format -i NHMineInfo.m -style=Google  
当然了,也可以直接格式化指定的行(-lines)

自定义格式,只需要在工程的目录下提供一个.clang-format文件,在其中指定自定义风格就可以了,这个.clang-format文件的编写,参考官方文档ClangFormat Style Options.

在Xccode8之前,github上有几个包装了clang-format的开源插件,可以用来方便的进行可视化和自动化的代码风格的调整 BBUncrustifyPlugin-Xcode,其代码格式化也是使用的llvm的clang-format(它用到的clang-format是0.39版本)
tupian1.png uncrustify

ClangFormat-Xcode
xcode8以后,已经不支持这种传统方式的插件了,所以需要格式化的话,需要另外的方式.

spacecommander

SpaceCommander为iOS开发团队提供了一个,无需任何手动修改,以统一格式将Object-C代码commit到git仓库的工具。它以git hooks的方式来工作.

何谓git Git-Git-Hooks, 其中文版在自定义-Git-Git-钩子,简单来说,git hooks就是Git 能在特定的重要动作发生时触发自定义脚本.理论上,只要我们编写一个git hooks,通过git hooks调用clang-format,那么就可以实现,提交到仓库的所有的代码都是格式化的!

spacecommander的安装

安装很简单,clone spacecommander的代码spacecommander到任意一个目录中.例如,我的电脑上是clone到/Users/yohunl/gitProjects/spacecommander中的.

可以看到,spacecommander是对cang-format的一层包装,让我们使用起来更方便. 为了后期方便使用,我们可以定义环境变量,在 ~/.profile中,添加一行

export SPACECOMMANDER="/Users/yohunl/gitProjects/spacecommander"  

那么,以后在命令行中可以直接使用 $SPACECOMMANDER 来代表这个目录.记住这个目录,等下我们需要运行这个目录里的脚本文件.

使用

打开你的某个项目的根目录.例如,示例工程 /Users/yohunl/projects/CustomIOS

$SPACECOMMANDER/setup-repo.sh

2016-11-21_14-36-48.jpg

备注: 如果你的项目包含了sub module,sub module并不会被添加hooks脚本,如果需要添加,需要进入module,再运行脚本命令

从上面脚本的运行中的回显,可以看到,该命令完成了两件事情 1 添加了隐藏文件 .clang-format文件的替身
2 添加了git hooks ,pre-commit,没有后缀名的那个,凡是带.sample的文件都是示例文件,那是git给你的示例demo,教你写hooks的.

2016-11-21_14-40-04.jpg

这两个文件是做什么的呢? 我们打开第一个的.clang-format文件,其内容如下:

---
# Custom options in the special build of clang-format (these are not standard options)
IndentNestedBlocks: false  
AllowNewlineBeforeBlockParameter: false

Language:        Cpp  
# BasedOnStyle:  Google
AccessModifierOffset: -1  
ConstructorInitializerIndentWidth: 4  
SortIncludes: false

AlignAfterOpenBracket: true  
AlignEscapedNewlinesLeft: true  
AlignOperands: false  
AlignTrailingComments: true

AllowAllParametersOfDeclarationOnNextLine: false  
AllowShortBlocksOnASingleLine: false  
AllowShortCaseLabelsOnASingleLine: false  
AllowShortFunctionsOnASingleLine: true  
AllowShortIfStatementsOnASingleLine: true  
AllowShortFunctionsOnASingleLine: All  
AllowShortLoopsOnASingleLine: true

AlwaysBreakAfterDefinitionReturnType: false  
AlwaysBreakTemplateDeclarations: false  
AlwaysBreakBeforeMultilineStrings: false

BreakBeforeBinaryOperators: None  
BreakBeforeTernaryOperators: false  
BreakConstructorInitializersBeforeComma: false

BinPackArguments: true  
BinPackParameters: true  
ColumnLimit: 0  
ConstructorInitializerAllOnOneLineOrOnePerLine: true  
DerivePointerAlignment: false  
ExperimentalAutoDetectBinPacking: false  
IndentCaseLabels: true  
IndentWrappedFunctionNames: false  
IndentFunctionDeclarationAfterType: false  
MaxEmptyLinesToKeep: 2  
KeepEmptyLinesAtTheStartOfBlocks: false  
NamespaceIndentation: Inner  
ObjCBlockIndentWidth: 4  
ObjCSpaceAfterProperty: true  
ObjCSpaceBeforeProtocolList: true  
PenaltyBreakBeforeFirstCallParameter: 10000  
PenaltyBreakComment: 300  
PenaltyBreakString: 1000  
PenaltyBreakFirstLessLess: 120  
PenaltyExcessCharacter: 1000000  
PenaltyReturnTypeOnItsOwnLine: 200  
PointerAlignment: Right  
SpacesBeforeTrailingComments: 1  
Cpp11BracedListStyle: true  
Standard:        Auto  
IndentWidth:     4  
TabWidth:        8  
UseTab:          Never  
BreakBeforeBraces: Custom  
BraceWrapping:  
    AfterClass: true
    AfterControlStatement: false
    AfterEnum: false
    AfterFunction: true
    AfterNamespace: true
    AfterObjCDeclaration: true
    AfterStruct: false
    AfterUnion: false
    BeforeCatch: false
    BeforeElse: false
    IndentBraces: false

SpacesInParentheses: false  
SpacesInSquareBrackets: false  
SpacesInAngles:  false  
SpaceInEmptyParentheses: false  
SpacesInCStyleCastParentheses: false  
SpaceAfterCStyleCast: false  
SpacesInContainerLiterals: true  
SpaceBeforeAssignmentOperators: true

ContinuationIndentWidth: 4  
CommentPragmas:  '^ IWYU pragma:'  
ForEachMacros:   [ foreach, Q_FOREACH, BOOST_FOREACH ]  
SpaceBeforeParens: ControlStatements  
DisableFormat:   false  
...

还记得第一部分,引子中说到的clang-format命令吧,这个文件就是clang-format命令的配置文件,参考官方文档ClangFormat Style Options,大体上就是配置一些代码风格 例如,其中的

IndentWidth:     4  

意思是代码的缩进宽度,这里默认的是4个空格,但是由于Google的最新的格式规范中,所有的缩进都是2了,所以我们要根据需要修改这个缩进为2.

第二个文件 pre-commit,是一个git的hooks,其内容如下:

#!/usr/bin/env bash
current_repo_path=$(git rev-parse --show-toplevel)  
repo_to_format="/Users/yohunl/projects/CustomIOS"  
if [ "$current_repo_path" == "$repo_to_format" ] && [ -e "/Users/yohunl/gitProjects/spacecommander"/format-objc-hook ]; then "/Users/yohunl/gitProjects/spacecommander"/format-objc-hook || exit 1; fi

这个文件,就是在你的工程每次 commit的时候,会预先先执行这里定义的脚本命令!,这段脚本很简单,就是每次commit的时候跑去本地的spacecommander文件夹里运行一个叫做format-objc-hook的文件.

当你commit一段代码的时候,如果代码不符合规范,那么会出提交不了,spacecommander会给出一些提示信息 2016-11-21_15-00-03.jpg 它会告诉你,这次提交的那个源码中的代码不符合规范,并且指出了格式化的命令.当然了,也可以忽略这次提交的格式化检查(git commit --no-verify,不过我们不建议这么做,因为这样做做,就失去了规范的意义,但是如果你修改第三方的代码,有时候,也是不可避免的,所以忽略还是有存在的意义的).

常用的spacecommander命令

space目录下提供了几个脚本命令

我们最常用的是其中的两个

format-objc-file.sh
这个脚本命令是用来格式化某一个文件的,例如我们要格式化文件 XXX.m,那么可以使用

//注意了,脚本和文件都是需要路径的.否则,默认在本路径下
/Users/yohunl/gitProjects/spacecommander/format-objc-file.sh  XXX.m

我们执行这个format-objc-file.sh命令,每次都要输入一大串路径,相对来说比较繁琐,所以我们可以在环境变量中添加一个alias

如果你用的是mac系统的默认终端,那么其环境变量的设置在 ~/.profile文件中,如果你和我一样使用的是zsh,那么就在文件~/.zshrc中添加

alias fmto="$SPACECOMMANDER/format-objc-file.sh"  

这样,下次在任何的路径下都可以使用

fmto XXX.m  

format-objc-files-in-repo.sh
这个脚本是用来格式化工程中所有的文件的,这个没有参数,直接使用,同理,我们也建立一个alias

alias fmtall="$SPACECOMMANDER/format-objc-files-in-repo.sh"  

这个命令,一般都是在初试的时候才使用的,也就是在刚刚建立spacecommander的时候,调用一次,规范化所有的已存在的代码.

这里,你可能会提出一个问题,我们现在工程都大量的使用pods来管理第三方库,那这个命令岂不是将pods中的源码也格式化了?

别担心,spacecommander已经考虑到这个问题了,你可以看spacecommander目录下的/lib/common-lib.sh,看不懂这个脚本没关系,其中有一段代码

其中的

echo "common_sh $files" | grep -v 'Pods/' | grep -v 'Carthage/' >&1  

这里就是用正则的方式来过滤掉Pods和Carthaage文件夹的内容的.

另外,你可能还有另一个疑问,那么如果第三方代码,不是通过pods导入的,而是直接添加源码到工程中的(事实上,这个情况很常见),那岂不是又是被格式化了?

还是刚刚那个脚本文件 common-lib.sh 其中也针对这种情况给出了处理的方式

function directories_to_ignore() {  
    if [ -f ".formatting-directory-ignore" ]; then
        files=$(echo "$files" | grep -v -f .formatting-directory-ignore)
    fi
}

这段大意是,在.formatting-directory-ignore文件中指定的目录将会被忽略,不会被格式化.那么要处理这种情况就很简单了,在工程下配置一个.formatting-directory-ignore文件,在其中,填写不需要被格式化的文件夹就可以了
这个文件的格式大体上如下所展示:

好了,到此,利用spacecommander来规范化代码,就已经可以了.尽情的享受规范化带来的便利吧.

后记

由于Google的新的规范都是建议缩进是2个空格了(以前都是4个).虽然有了spacecommander这样的规范化的脚本,但是,我们平时桥码代码的时候,编译器还是4个空格的话,就会很不方便,所以还需要调整下Xcode的配置,将其缩进调整为2空格.

参考文档

spacecommander
由spacecommander这个库所想到的自动化管理
spacecommander翻译
shell脚本命令
raywenderlich规范
github规范
google规范
michalrus规范 https://gist.github.com/wangkuiyi/7379a242f0d4089eaa75
https://techblog.badoo.com/blog/2015/09/21/clang-format-as-a-guard-for-objective-c-code-style/
http://www.reviewcode.cn/article.html?reviewId=1
http://www.jianshu.com/p/a6cfe69c5783

插件
https://github.com/benoitsan/BBUncrustifyPlugin-Xcode
https://github.com/Cue/ocstyle
https://github.com/kzaher/RegX
非常完整的包括工程的组织方式建议

swift的代码规范的格式化控件
https://github.com/haaakon/SwiftFormat
https://github.com/Jintin/Swimat 支持xcode8的
https://github.com/qfish/XAlign 代码行对齐的

swift的代码规范
https://github.com/github/swift-style-guide github官方推荐的
https://github.com/raywenderlich/swift-style-guide raywenderlich的推荐规范

oc版本的
https://github.com/raywenderlich/objective-c-style-guide

最近的文章

APP逆向分析之钉钉抢红包插件的实现-iOS篇

花费了很多天的原创文章,转载请注明出处https://yohunl.com/ding-ding-qiang-hong-bao-cha-jian-iospian/ ,谢谢! 网络上关于微信红包的分…

继续阅读
更早的文章

iOS监控/监测/监听文件/文件夹的变化 检测文件变化

我们有些时候,需要监测一个文件/文件夹的变化,例如在某个文件被修改的时候,可以获取到通知,或者我们有个播放列表是扫描某个文件夹下的所有文件,那么当这个目录新添或者删除一些文件后,我们的播放列表要…

继续阅读