总览
背景知识
Objective-C是一个C语言的扩展语言,非常动态,非常的“面向对象”,它被设计成既拥有复杂的面向对象设计理念又可以轻松使用与阅读的语言,也是MacOSX和iPhone开发的首选语言。
Cocoa是MacOSX的主要应用框架,提供迅速开发各种功能的MacOSX应用的Objective-C类集合。
Apple已经有一个很好也被广泛接受的Objective-C的编程规范,Google也有类似的C++编程规范,这份Objective-C编程规范很自然是Apple和Google的共同推荐的组合。
注意所有已在Google的C++编程规范里的禁用条款在Objective-C里也适用,除非本文档明确指出反对意见。
本文档旨在描述可供可适用于所有MacOSX代码的Objective-C(包括Objective-C++)编码规范和实践。规范中的许多条款已经改进也不断的被其他的项目和团队所证明其指导性。Google的相关开源项目都遵守此规范。
Google已经发布了一份作为GoogleToolboxforMacproject(文档中简称为GTM)的组成部分的遵守本规范的开源代码。这份开放代码也是本文很好的例证。
示例
人们说一个例子胜过千言万语,所以就让我们用例子来让你感受以下编码规范的风格,留间距,命名等等。
下例是一份头文件,展示对@interface声明正确的注释和留间距
Java代码
- // GTMFoo.h
- // FooProject
- //
- // Created by Greg Miller on 6/13/08.
- // Copyright 2008 Google, Inc. All rights reserved.
- //
- #import
- // 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 GTMFoo : NSObject {
- @private
- NSString *foo_;
- NSString *bar_;
- }
- // Returns an autoreleased instance of GMFoo. See -initWithString: for details
- // about the argument.
- + (id)fooWithString:(NSString *)string;
- // Designated initializer. |string| will be copied and assigned to |foo_|.
- - (id)initWithString:(NSString *)string;
- // Gets and sets the string for |foo_|.
- - (NSString *)foo;
- - (void)setFoo:(NSString *)newFoo;
- // Does some work on |blah| and returns YES if the work was completed
- // successfuly, and NO otherwise.
- - (BOOL)doWorkWithString:(NSString *)blah;
- @end
下例是一份源文件,展示对接口的@implementation的实现的正确注释和留间隔。它也包括了主要方法如getters,setters,init,和dealloc的相关实现。
Java代码
- //
- // GTMFoo.m
- // FooProject
- //
- // Created by Greg Miller on 6/13/08.
- // Copyright 2008 Google, Inc. All rights reserved.
- //
- #import "GTMFoo.h"
- @implementation GTMFoo
- + (id)fooWithString:(NSString *)string {
- return [[[self alloc] initWithString:string] autorelease];
- }
- // Must always override super's designated initializer.
- - (id)init {
- return [self initWithString:nil];
- }
- - (id)initWithString:(NSString *)string {
- if ((self = [super init])) {
- foo_ = [string copy];
- bar_ = [[NSString alloc] initWithFormat:@"hi %d", 3];
- }
- return self;
- }
- - (void)dealloc {
- [foo_ release];
- [bar_ release];
- [super dealloc];
- }
- - (NSString *)foo {
- return foo_;
- }
- - (void)setFoo:(NSString *)newFoo {
- [foo_ autorelease];
- foo_ = [newFoo copy];
- }
- - (BOOL)doWorkWithString:(NSString *)blah {
- // ...
- return NO;
- }
- @end
间隔与格式化
空格对tab键,仅使用空格,缩进两个。
我们使用空格用于缩进,不要在编码时使用tab键,你应该设置你的编辑器将tab键转换成对应的空格。
行长度
代码中的每行文本不要超过80个字符的长度。
尽管Objective-C正变得比C++更加繁冗,为了保持规范的互通性,我们还是决定保持80字符长度的限制。这比你想象中的更容易做到。
我们知道本条款是有争议的,但已有此多的代码已经遵从了本条款,即使只是保持一致性也是一个充足的理由。
你可以在Xcode里清楚地发现代码中的违规,设置Xcode>Preferences>TextEditing>Showpageguide.(之后就可以在代码编辑区域里看到一条指定字符长度的指示线了)
方法声明与定义
留一个空格在-或+和返回类型之间,但参数列表里的参数之间不要留间隔。
方法应该写成这样:
Java代码
- - (void)doSomethingWithString:(NSString *)theString {
- ...
- }
星号前的空格是可选的,你可以根据原来的代码风格自行决定。
如果参数过多,推荐每个参数各占一行。使用多行的情况下,在参数前加冒号用于对齐:
(很遗憾这里仅有Google Chrome浏览器能看出是冒号对齐的......)
Java代码
- - (void)doSomethingWith:(GTMFoo *)theFoo
- rect:(NSRect)theRect
- interval:(float)theInterval {
- ...
- }
当第一个关键字比其他的短时,后续行至少缩进四个空格。这样你可以让后续的关键字垂直对齐,而不是用冒号对齐:
Java代码
- - (void)short:(GTMFoo *)theFoo
- longKeyword:(NSRect)theRect
- evenLongerKeyword:(float)theInterval {
- ...
- }
方法调用
方法调用的格式和方法声明时的格式时一致的,如果格式风格可选,遵从原有代码的风格。
调用应该将所有参数写在一行:
Java代码
- [myObject doFooWith:arg1 name:arg2 error:arg3];
或者每个参数一行,用冒号对齐:
(对齐效果如前说明......)
Java代码
- [myObject doFooWith:arg1
- name:arg2
- error:arg3];
不要使用如下风格的写法
Java代码
- [myObject doFooWith:arg1 name:arg2 // some lines with >1 arg
- error:arg3];
- [myObject doFooWith:arg1
- name:arg2 error:arg3];
- [myObject doFooWith:arg1
- name:arg2 // aligning keywords instead of colons
- error:arg3];
在声明和定义时,如果因为关键字长度使就算有四个空格在前仍然无法用冒号对齐,那么就让后续行缩进四个空格而不是冒号对齐:
Java代码
- [myObj short:arg1
- longKeyword:arg2
- evenLongerKeyword:arg3];
权限控制符@public和@private缩进一个空格.
类似C++的public,protected,private:
Java代码
- @interface MyClass : NSObject {
- @public
- ...
- @private
- ...
- }
- @end
异常
每个异常标签的@和开括号({)写在统一行,标签和开括号间隔一个空格,同样适用于@catch语句。
如果你决定使用Objective-C的异常,那么就按如下格式,不过你最好先看看AvoidThrowingExceptions(见后)条款,了解为何你不应使用异常。
Java代码
- @try {
- foo();
- }
- @catch (NSException *ex) {
- bar(ex);
- }
- @finally {
- baz();
- }
命名
命名规则对于维护代码来说是非常重要的。Objective-C方法名往往很长,不过这也有好处,读代码就像读散文(放屁),让很多注释变得毫无意义。
写纯Objective-C代码时,我们基本上遵守标准Objective-Cnamingrules,这些规则和C++的规则有很大的不同。比如Google的C++代码规范推荐变量名构词之间使用下划线隔开,而本文档推荐驼峰法,也是Objective-C社区的标准。
所有类,类别,方法,以及变量如包括缩写,则缩写部分使用全大写的缩写(Initialisms)形式。这遵守Apple的标准,比如URL,TIFF以及EXIF。
当写Objective-C++代码时,情况就不是那么单一了。许多项目需要实现带一些Objective-C代码的跨平台的C++APIs或者连接后台的C++代码与前台的原生Cocoa代码.这会造成两种规范直接冲突。
我们的解决方法是根据方法/函数风格来决定。如果在@implementation块,就使用Objective-C的命名规则;如果在C++的方法实现块,就使用C++的命名规则。避免了实体变量和本地变量在一个函数内命名规则冲突的情况,而这种情况是对可读性的极大损害。
文件命名
文件名反映了它所包含的实现类的名字,遵从你所在项目的习惯。