留白和格式
空格vs.制表符
我们使用空格缩进。不要在代码中使用制表符。你应该将编辑器设置成自动将制表符替换成空格。
行宽
尽量让你的代码保持在80列之内。
通过设置Xcode > Preferences > Text Editing > Show page guide,来使越界更容易被发现。
方法声明和定义
/ +和返回类型之间须使用一个空格,参数列表中只有参数之间可以有空格。
-(void)doSomethingWithString:(NSString *)theString {
...
}
星号前的空格是可选的。当写新的代码时,要与先前代码保持一致。
- (void)doSomethingWith:(GTMFoo *)theFoo
rect:(NSRect)theRect
interval:(float)theInterval {
...
}
如果一行有非常多的参数,更好的方式是将每个参数单独拆成一行。如果使用多行,将每个参数前的冒号对齐。
方法调用
方法调用应尽量保持与方法声明的格式一致。当格式的风格有多种选择时,新的代码要与已有代码保持一致。
调用时所有参数应该在同一行:
[myObject doFooWith:arg1 name:arg2 error:arg3];
或者每行一个参数,以冒号对齐:
[myObject doFooWith:arg1
name:arg2
error:arg3];
不要使用下面的缩进风格:
[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];
方法定义与方法声明一样,当关键字的长度不足以以冒号对齐时,下一行都要以四个空格进行缩进。
[myObj short:arg1
longKeyword:arg2
evenLongerKeyword:arg3];
协议名
类型标识符和尖括号内的协议名之间,不能有任何空格。
这条规则适用于类声明、实例变量以及方法声明。例如:
@interface MyProtocoledClass : NSObject {
@private
id delegate_;
}
- (void)setDelegate:(id)aDelegate;
@end
块(闭包)
块(block)适合用在target/selector模式下创建回调方法时,因为它使代码更易读。块中的代码应该缩进4个空格。
取决于块的长度,下列都是合理的风格准则:
-如果一行可以写完块,则没必要换行。
-如果不得不换行,关括号应与块声明的第一个字符对齐。
-块内的代码须按4空格缩进。
-如果块太长,比如超过20行,建议把它定义成一个局部变量,然后再使用该变量。
-如果块不带参数,^{
之间无须空格。如果带有参数,^(
之间无须空格,但) {
之间须有一个空格。
-块内允许按两个空格缩进,但前提是和项目的其它代码保持一致的缩进风格。
// The entire block fits on one line.
[operation setCompletionBlock:^{ [self onOperationDone]; }];
// The block can be put on a new line, indented four spaces, with the
// closing brace aligned with the first character of the line on which
// block was declared.
[operation setCompletionBlock:^{
[self.delegate newDataAvailable];
}];
// Using a block with a C API follows the same alignment and spacing
// rules as with Objective-C.
dispatch_async(fileIOQueue_, ^{
NSString* path = [self sessionFilePath];
if (path) {
// ...
}
});
// An example where the parameter wraps and the block declaration fits
// on the same line. Note the spacing of |^(SessionWindow *window) {|
// compared to |^{| above.
[[SessionService sharedService]
loadWindowWithCompletionBlock:^(SessionWindow *window) {
if (window) {
[self windowDidLoad:window];
} else {
[self errorLoadingWindow];
}
}];
// An example where the parameter wraps and the block declaration does
// not fit on the same line as the name.
[[SessionService sharedService]
loadWindowWithCompletionBlock:
^(SessionWindow *window) {
if (window) {
[self windowDidLoad:window];
} else {
[self errorLoadingWindow];
}
}];
// Large blocks can be declared out-of-line.
void (^largeBlock)(void) = ^{
// ...
};
[operationQueue_ addOperationWithBlock:largeBlock];```
##命名
对于易维护的代码而言,命名规则非常重要。Objective-C的方法名往往十分长,但代码块读起来就像散文一样,不需要太多的代码注释。
任何的类、类别、方法以及变量的名字中都使用全大写的[首字母缩写](http://en.wikipedia.org/wiki/Initialism)。这遵守了苹果的标准命名方式,如URL、TIFF以及EXIF。
当编写纯粹的Objective-C代码时,我们基本遵守标准的[Objective-C naming rules](http://developer.apple.com/documentation/Cocoa/Conceptual/CodingGuidelines/CodingGuidelines.html)
###文件名
文件名须反映出其实现了什么类–包括大小写。遵循你所参与项目的约定。
文件的扩展名应该如下:
| `.h`| C/C++/Objective-C的头文件|
| ----- | ---------------------- |
| `.m`| Ojbective-C实现文件|
| `.mm` | Ojbective-C++的实现文件|
| `.cc` |纯C++的实现文件|
| `.c`|纯C的实现文件|
类别的文件名应该包含被扩展的类名,如:`GTMNSString+Utils.h`或``GTMNSTextView+Autocomplete.h``。
###类名
类名(以及类别、协议名)应首字母大写,并以驼峰格式分割单词。
*应用层*的代码,应该尽量避免不必要的前缀。为每个类都添加相同的前缀无助于可读性。当编写的代码期望在不同应用程序间复用时,应使用前缀(如:`GTMSendMessage`)。
###类别名
类别名应该有两三个字母的前缀以表示类别是项目的一部分或者该类别是通用的。类别名应该包含它所扩展的类的名字。
比如我们要基于`NSString`创建一个用于解析的类别,我们将把类别放在一个名为`GTMNSString+Parsing.h`的文件中。类别本身命名为`GTMStringParsingAdditions`(是的,我们知道类别名和文件名不一样,但是这个文件中可能存在多个不同的与解析有关类别)。类别中的方法应该以`gtm_myCategoryMethodOnAString:`为前缀以避免命名冲突,因为Objective-C只有一个名字空间。如果代码不会分享出去,也不会运行在不同的地址空间中,方法名字就不那么重要了。
类名与包含类别名的括号之间,应该以一个空格分隔。
### Objective-C方法名
方法名应该以小写字母开头,并混合驼峰格式。每个具名参数也应该以小写字母开头。
方法名应尽量读起来就像句子,这表示你应该选择与方法名连在一起读起来通顺的参数名。(例如,`convertPoint:fromRect:`或`replaceCharactersInRange:withString:`)。
访问器方法应该与他们`要获取的`成员变量的名字一样,但不应该以get作为前缀。例如:
```objective-c
- (id)getDelegate;// AVOID
- (id)delegate;// GOOD
这仅限于Objective-C的方法名。C++的方法与函数的命名规则应该遵从C++风格指南中的规则。
变量名
变量名应该以小写字母开头,并使用驼峰格式。类的成员变量应该以下划线作为后缀。如:myLocalVariable
、myInstanceVariable_
。如果不能使用Objective-C 2.0的@property
,使用KVO/KVC绑定的成员变量可以以一个下划线作为前缀。
普通变量名
对于静态的属性(int
或指针),不要使用匈牙利命名法。尽量为变量起一个描述性的名字。不要担心浪费列宽,因为让新的代码阅读者立即理解你的代码更重要。例如:
-错误的命名:
int w;
int nerr;
int nCompConns;
tix = [[NSMutableArray alloc] init];
obj = [someObject object];
p = [network port];
-正确的命名
int numErrors;
int numCompletedConnections;
tickets = [[NSMutableArray alloc] init];
userInfo = [someObject object];
port = [network port];
实例变量
实例变量应该混合大小写,并以下划线作为后缀,如usernameTextField_
。然而,如果不能使用Objective-C 2.0(操作系统版本的限制),并且使用了KVO/KVC绑定成员变量时,我们允许例外(译者注:KVO=Key Value Observing,KVC=Key Value Coding
)。这种情况下,可以以一个下划线作为成员变量名字的前缀,这是苹果所接受的键/值命名惯例。如果可以使用Objective-C 2.0,@property
以及@synthesize
提供了遵从这一命名规则的解决方案。
常量
常量名(如宏定义、枚举、静态局部变量等)应该以小写字母k
开头,使用驼峰格式分隔单词,如:kInvalidHandle,kWritePerm
。
注释
文件注释
每个文件的开头以文件内容的简要描述起始,紧接着是作者,最后是版权声明和/或许可证样板。
版权信息及作者
每个文件应该按顺序包括如下项:
-文件内容的简要描述
-代码作者
-版权信息声明(如:Copyright 2008 Google Inc.
)
-必要的话,加上许可证样板。为项目选择一个合适的授权样板(例如,Apache 2.0, BSD, LGPL, GPL
)。
如果你对其他人的原始代码作出重大的修改,请把你自己的名字添加到作者里面。当另外一个代码贡献者对文件有问题时,他需要知道怎么联系你,这十分有用。
声明部分的注释
每个接口、类别以及协议应辅以注释,以描述它的目的及与整个项目的关系。
// A delegate for NSApplication to handle notifications about app
// launch and shutdown. Owned by the main app controller.
@interface MyAppDelegate : NSObject {
...
}
@end
如果你已经在文件头部详细描述了接口,可以直接说明“完整的描述请参见文件头部”,但是一定要有这部分注释。
另外,公共接口的每个方法,都应该有注释来解释它的作用、参数、返回值以及其它影响。
为类的线程安全性作注释,如果有的话。如果类的实例可以被多个线程访问,记得注释多线程条件下的使用规则。
实现部分的注释
使用|
来引用注释中的变量名及符号名而不是使用引号。
这会避免二义性,尤其是当符号是一个常用词汇,这使用语句读起来很糟糕。例如,对于符号count
:
// Sometimes we need |count| to be less than zero.
或者当引用已经包含引号的符号:
// Remember to call |StringWithoutSpaces("foo bar baz")|
Objective-C特性
属性
命名
属性所关联的实例变量的命名必须遵守以下划线作为后缀的规则。属性的名字应该与成员变量去掉下划线后缀的名字一模一样。
使用@synthesize
指示符来正确地重命名属性。
@interface MyClass : NSObject {
@private
NSString *name_;
}
@property(copy, nonatomic) NSString *name;
@end
@implementation MyClass
@synthesize name = name_;
@end
位置
属性的声明必须紧靠着类接口中的实例变量语句块。属性的定义必须在@implementation
的类定义的最上方。他们的缩进与包含他们的@interface
以及@implementation
语句一样。
@interface MyClass : NSObject {
@private
NSString *name_;
}
@property(copy, nonatomic) NSString *name;
@end
@implementation MyClass
@synthesize name = name_;
- (id)init {
...
}
@end
点引用
点引用是地道的Objective-C 2.0风格。它被使用于简单的属性set
、get
操作,但不应该用它来调用对象的其它操作。
正确的做法:
NSString *oldName = myObject.name;
myObject.name = @"Alice";
错误的做法:
NSArray *array = [[NSArray arrayWithObject:@"hello"] retain];
NSUInteger numberOfItems = array.count;// not a property
array.release;// not a property