Google 的 Objective-C 代码规范指南
注意事项
显示在本指南中的隐藏细节
这个风格指南包含很多最初不可见的细节。它们被标记为三角形图标,你可以在左边看到。现在点击它,你应该会看到“万岁”出现在下面。
背景
Objective-C是一种很动态的、面向对象的C语言扩展。它被设计成易用易读,同时支持复杂的面向对象设计。它是Mac OS X和iPhone上开发新应用的主要开发语言
Cocoa是在Mac OS X平台上的一个主要的应用框架。这是一个提供给全功能Mac OS X应用程序的快速开发的Objective-C类集合。
Apple已经为Objective-C编写了一本很好的、被广泛接受的编码指南;Google也为C++写了一本类似的指南。这本Objective-C指南意在成为一个对Apple和Google的一般建议的自然组合。因此,在读这本指南之前,确定你已经度过:
- Apple的Cocoa编码指南
- Google的开源C++设计指南
注意:所有在Google C++指南中禁用的在Objective-C++里也一样,除非本文特别指出。
这个文档是为了描述用于所有Mac OS X代码中的Objective-C(和Objective-C++)编码指南和实践的。这些指南的很多地方已经进化并在其他项目和团队已经过时。谷歌开发的开源项目符合本指南中的要求。
谷歌已经发布了符合这些准则的,作为Mac项目的谷歌工具箱(以下简称GTM)的一部分的开源代码。意味着要在不同项目共享的代码是一个包含在这个库中很好的候选。
注意:这本指南不是一个Objective-C教程。我们假设读者对这么语言很熟悉。如果你是一个Objective-C初学者或需要复习,请阅读的Objective-C编程语言这本书。
例子
他们说一个例子胜过千言万语,让我们开始用一个例子让你感受Objective-C的风格,间距,命名等。
一个头文件的例子,展示了@interface声明的正确注释和间距:
#import <Foundation/Foundation.h> // A sample class demonstrating good Objective-C style. All interfaces, // categories, and protocols (read: all top-level declarations in a header) // MUST be commented. Comments must also be adjacent to the object they're // documenting. // // (no blank line between this comment and the interface) @interface Foo : NSObject { @private NSString *_bar; NSString *_bam; } // Returns an autoreleased instance of Foo. See -initWithBar: for details // about |bar|. + (id)fooWithBar:(NSString *)bar; // Designated initializer. |bar| is a thing that represents a thing that // does a thing. - (id)initWithBar:(NSString *)bar; // Gets and sets |_bar|. - (NSString *)bar; - (void)setBar:(NSString *)bar; // Does some work with |blah| and returns YES if the work was completed // successfully, and NO otherwise. - (BOOL)doWorkWithBlah:(NSString *)blah; @end一个源文件的例子,展示了一个接口的@implementation的正确注释和间距。它也包含一些重要方法,像getters、setters、init和dealloc的参考实现:
#import "Foo.h" @implementation Foo + (id)fooWithBar:(NSString *)bar { return [[[self alloc] initWithBar:bar] autorelease]; } // Must always override super's designated initializer. - (id)init { return [self initWithBar:nil]; } - (id)initWithBar:(NSString *)bar { if ((self = [super init])) { _bar = [bar copy]; _bam = [[NSString alloc] initWithFormat:@"hi %d", 3]; } return self; } - (void)dealloc { [_bar release]; [_bam release]; [super dealloc]; } - (NSString *)bar { return _bar; } - (void)setBar:(NSString *)bar { [_bar autorelease]; _bar = [bar copy]; } - (BOOL)doWorkWithBlah:(NSString *)blah { // ... return NO; } @end在@interface、@implementation和@end前后的空行是可选的。如果你的@interface声明了实参,那么右括号之后需要有一个空行。
除非interface或implementation很短,比如,当定义一小部分私有方法或一个桥接类时,添加空行通常有利于可读性。
间距和格式
空格(spacing)与制表符(tab)
▶每行的宽度
▶方法的声明和定义
▶方法调用
▶@public与@private
▶Exceptions
▶在单独一行时,使用 @ 标签格式化 exceptions,@ 标签和开放括号 ({) 间加一个空格,在@catch 和 对象捕获声明之间也是。
Protocols
▶在类型标识符和封装在尖括号中的 Protocols 名称之间不应该有空格。
Blocks
▶Blocks 在创建回调函数时更倾向于目标选择器模式,这可以让代码更易读。块内的代码应缩进4个空格。
命名
命名规则对于代码的可维护性是非常重要的。Objective-C的方法命名趋向于超长命名,但这会带来良好的代码阅读感受,就像读散文一样,同时还避免了很多不必要的注释。
在撰写纯粹的Objective-C代码时,我们主要是遵循标准的Objective-C命名规范。这些命名方针可能和C++的命名规范相去甚远。比如,Google的C++规范中推荐在变量名中的单词间使用下划线,而Objective-C的规范推荐使用驼峰命名法,这也是在Objective-C社区中的标准做法。
任何类、目录、方法或者变量的名字都应该将其中的首字母缩略词设为大写。下面是苹果官方使用的大写的首字母缩略词:URL,TIFF和EXIF。
然而,在编写Objective-C++代码时,往往并非能完全按照规范来进行。很多项目会使用Objective-C,或是Cocoa,或是C++后端与原生的Cocoa前端通信的方式来实现跨平台的C++ API。这就导致了两种语言规范直接冲突的情况。我们的解决方法是依据方法/函数具体实现的方式来决定命名。如果你在一个@implementationblock里面,就使用Objective-C的命名规范。如果你在一个C++类里面实现一个方法,就使用C++命名规范。这就避免了在一个函数中实例变量和本地变量的命名规则混合使用的情况,减少了对代码的可阅读性造成的极大损害。
文件名
▶文件名应该反映其中包含的类实现的名称,按照你项目中的约定且大小写相关。
Objective-C++
▶在一个源码文件中, Objective-C++ 遵循你实现的函数/方法的风格。
类名
▶类名(类别和协议名称)应为大写,并开始使用大小写混合以区分单词。
分类名称
▶分类名称应该以一个2到3个字母的前缀开始,识别分类是项目的一部分,还是打开使用。分类,名称应该结合它扩展的类的名称。
Objective-C 方法名称
▶方法名称应该以小写字母开头,混合大小写。每个命名参数也应该以小写字母开头。
变量名
▶变量名以小写字母开头,混合大小写以区分单词。实例变量以下划线开头。例如:myLocalVariable,_myInstanceVariable。
注释
尽管写的时候很痛苦,但是他们对于保持你代码的可读性来说绝对是至关重要的。下面几条规则描述了你应该什么时候在什么地方加注释。但是要记住,虽然注释很重要,但是最好的代码都是自注释的。给变量和类型一个有意义的名字,要比用一个难懂的名字然后再费力的用注释来解释好得多。
写注释的时候,要写给你的读者:下一个要读懂你代码的贡献者。多写点吧——下个没准就是你自己!
记住所有c++编程规范里列出的规则和协定在这里也是生效的,下面是几点补充。
File Comments文件注释
▶声明注释Declaration Comments
▶实现声明Implementation Comments
▶对象所有权Object Ownership
▶被申明在头文件的变量必须是私有的
当变量被申明在头文件时,这个变量必须被标记为@private
标识初始化器
要描述或者标识好初始化器
重写初始化器
当你写一个包含init()方法的的子类时,一定要确定重写了父类的初始化器
重写NSObject方法的位置
强连建议将重写NSObject的方法放到@implementation注记的上面
初始化
不要在init方法中把变量初始化为0或者nil,这样做完全是多余的
避免处理new方法
不要尝试调用NSObject类的new()方法,也不要在他的子类中复写这个方法。我们可以利用他的init方法来实例化这些保留对象
保持公有API简单
要保持你的类尽量简单:避免过分渲染APIs.如果有的方法没必要公开,那就不要公开.要利用private去避免把公有头弄得杂乱
#import 和 #include
要引入Objective-C/Object-C++头文件时用#import;要引入C/C++头文件时用#include.
利用跟框架
在独立文件之上应该包含跟框架
创建完了即可销毁释放
当创建一些临时对象时,在创建的那一行销毁并释放比在同一个方法内下创建然后过一会儿销毁更好
释放并保留
对象的业务遵循释放并保持规则
在执行init或者dealloc方法时避免访问器
当init和dealloc方法正在执行时,子类的实例化可能处在一个不稳定的状态,所以在这些方法中的代码尽量避免调用其他访问器
在声明的次序中 Dealloc 实例变量
▶Setters 复制 NSStrings
▶避免抛出异常
▶nil 检查
▶BOOL 陷阱
▶属性
▶在下列情况下请注意,直接使用@property注解是被允许的:属性是将会限制你代码允许在iPhone和Mac OS X 10.5(雪豹)以上版本的Objective-C 2.0的特性。点符号只有声明@property才允许被访问。
没有示例变量的接口
▶接口中除了空大括号之外,请不要声明任何实例变量。
自动合成实例变量
▶使用自动合成实例变量是被允许的。编写的代码必须支持更早之前的编译工具链版本(Xcode 4.3或更早的版本亦或GCC编译)或者应该直接使用@synthesize注解调用从协议中继承来的属性。
自动引用计数(ARC)
▶由于项目使用的是Xcode 4.2或更高版本,并将只运行在64位版Mac OS X 10.7 和 iOS 5.0及更高版本中,ARC 是优先的。手动引用计数在支持早期环境中的归零弱指针时将不可用。
需要 ARC 的类应该包含一个预处理指令来防止编译使用手动引用计数。
象 __unsafe _unretained 和 __weak 的所有权限定符应该在变量名的前面。没必要为变量指定 __strong,因为它是默认的。另一方面,属性应该始终指定 strong 关键字而不是依赖于编译器的默认值。
被编译的文件使用 ARC 时需要有预处理指令以防止编译没有 ARC。请参见下方的代码片段以了解详情。
NSNumber字面值
▶对于利用Xcode4.4或以上版本创建的C语言项目来说,运用NSNumber字面值是被允许的。然而使用这种做法将会限制你代码对于其他工具链的可移植性。
Cocoa模式
委派模式
▶委派对象不应该被保留。
模型、视图、控制器
▶将模型从视图中分离。将控制器从视图和模型中分离。使用@protocols注解回调APIs。
历史说明
后缀下划线 vs 前置下划线
▶后缀下划线曾经用于表示示例变量的名称。