--参考:https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/CodingGuidelines/Articles/NamingIvarsAndTypes.html +Google Style Guide + http://www.tuicool.com/articles/YVv2eaZ
--前言:文章主要参考<Google Objective-C Style Guide>,以及一些良好的Obj-c风格。(<Google Objective-C Style Guide>文档主要是结合苹果的<Cocoa Coding Guidelines> 和<Google's Open Source C++ Style Guide>而来,一些在后俩者里适用的规则同样适用于它)。
============Spacing And Formatting/风格============
--Spaces vs. Tabs(空行)
总结:只使用空格,每一行代码缩进一个/四个空格。
我们使用空格进行缩进。不要在代码中使用制表符。你应该将你的文本编辑器设置成自动将制表符替换成空格(xcode preferences)。
个人习惯:当代码(括号等运算符)行首与上一行有缩进的时候,习惯空一行,例如
if(条件){
//code
}
--运算符
总结:1.二元运算符 和参数之间需要放置一个空格,
2.一元运算符 、 强制类型转换和参数之间不放置空格。
void *ptr = &value + 10 * 3;
NewType a = (NewType) b;
--关键字:
关键字之后圆括号之前 + '分隔符'和代码之间 需要放置一个空格。
for (int i = 0; i < 10; i++) {//注意看';'和代码之间有空格
doCoolThings();
}
NSArray *names = @[ @"Brian", @"Matt", @"Chris", @"Alex", @"Steve", @"Paul" ]; // ','和对象之间有个空格
--条件语句:
if (!error) {
return success;
}
// 不良的风格:
if (!error)
return success;
// or
if (!error) return success;
--三元运算符:
长的三元运算符应使用圆括号括起来。三元运算符仅用于赋值和做参数。
Blah *a = (stuff == thing ? foo : bar);
//合并的 nil 三元运算符应该尽量避免。
//不良的风格:
Blah *b = thingThatCouldBeNil ? nil : defaultValue;
//多分支条件应该使用 if 语句或重构为实例变量。
//良好的风格:
result = a > b ? x : y;
//不良的风格:
result = a > b ? x = c > d ? c : d : y;
--Line Length
总结:代码中的每行最多有80个字符。
即使Objective-C比C++更加冗长,为了保证本指南的可操作性,我们决定保持每行宽度为80列。这比你想的要简单。 我们意识到这条规则是有争议的,但很多已经存在的代码坚持了本规则,因此我们觉得保证一致性更重要。 通过设置Xcode>Preferences>Text Editing>Show page guide,来使越界更容易被发现。
--方法声明与定义
总结:-或者+与返回类型之间,需要有空格。no spacing in the parameter list except between parameters(类似xx:(int ) arg1 xx:(int)arg1)。
方法应该像这样:
- (void)doSomethingWithString:(NSString *)theString {
...
}
星号前的空格是可选的。当写新的代码时,要与原的代码一致。
如果一行有非常多的参数,更好的方式是将每个参数单独拆成一行。如果使用多行,将每个参数前的冒号对齐。 不再举例。注意,当第一个关键字比其它的短时,保证下一行至少有4个空格的缩进。这样可以使关键字垂直对齐,而不是使用冒号对齐:
- (void)short:(GTMFoo *)theFoo
longKeyword:(NSRect)theRect
evenLongerKeyword:(float)theInterva {
...
}
--Method Invocations总结:方法调用的格式与方法声明的格式非常相似。当格式的风格有多种选择时,新的代码要与已经存在的代码保持一致。
方法调用时,所有参数应该在同一行。
[myObject doFooWith:arg1 name:arg2 error:arg3];
或者每行一个参数,以冒号对齐:
[myObject doFooWith:arg1
name:arg2
error:arg3];
与方法声明一样,当the keyword lengths make it impossible to align colonsand still have four leading spaces, indent later lines by four spaces and align keywords after the firstone, instead of aligning the colons。
[myObj short:arg1
longKeyword:arg2
evenLongerKeyword:arg3];
--@public and @private
总结:@public以及@private访问标识符应该以一个空格缩进。
与C++中的public, private以及protected非常相似。
@interface MyClass : NSObject {
@public
...
@private
...
}
@end
--Exceptions
总结:每个@标签应该有独立的一行,在@与{}之间需要有一个空格。@catch与被捕捉到的异常对象的声明之间也要有一个空格。
如果你必须使用Objective-C的异常,按下面的格式进行编码代码。然后,请参见 Avoid Throwing Exceptions来了解不应该使用异常的原因。
@try {
foo();
}
@catch (NSException *ex) {
bar(ex);
}
@finally {
baz();
}
--Protocols
总结:尖括号所包括的协议名称与前面的类型标识之间不应该有空格。
这条规则也同样适用于类声明、成员变量以及方法(前面的协议)声明。例如
@interface MyProtocoledClass : NSObject<NSWindowDelegate> {
@private
id<MyFancyDelegate> _delegate;
}
- (void)setDelegate:(id<MyFancyDelegate>)aDelegate;
@end
--Blocks
总结:块中的代码缩进4个空格;
根据块长度,有几个不同的样式:
--如果块长度适合一行,no wrapping is necessary.
--如果块需要warp, 右括号对其“第一行”首字母.
--block中的代码缩进4个空格.
--如果块非常大,例如多余20行,最好把它像个局部变量一样,单独起一行。
--如果快没有参数, ^{.没有空格在;如果有参数, ^(它没有空格,它) { 有空格。
--调用时,有多个inlined blocks,就左对其,缩进4个空格。
--块里面也允许缩进2个空格,如果项目规则都是缩进2个空格.
// 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];
// An example with multiple inlined blocks in one invocation.
[myObject doSomethingWith:arg1
firstBlock:^(Foo *a) {
// ...
}
secondBlock:^(Bar *b) {
// ...
}];
--“包含符号”Container Literals
总结:在xcode4.4以后,数组和字典推荐使用Container Literals,
1.如果只占一行,方括号、大括号俩边各一个空格;
2.字典字面值的键和冒号之间没有空格,冒号和值之间有一个空格;
3.如果它内容占多行,使用xcode默认的规范,每行对其大括号、方括号。
NSArray *names = @[ @"Brian", @"Matt", @"Chris", @"Alex", @"Steve", @"Paul" ];
NSDictionary *productManagers = @{ @"iPhone": @"Kate", @"iPad": @"Kamal", @"Mobile Web": @"Bill" };
NSNumber *shouldUseLiterals = @YES;
NSNumber *buildingZIPCode = @10018;
//不良的风格:
NSArray *names = [NSArray arrayWithObjects:@"Brian", @"Matt", @"Chris", @"Alex", @"Steve", @"Paul", nil];
NSDictionary *productManagers = [NSDictionary dictionaryWithObjectsAndKeys: @"Kate", @"iPhone", @"Kamal", @"iPad", @"Bill", @"Mobile Web", nil];
NSNumber *shouldUseLiterals = [NSNumber numberWithBool:YES];
NSNumber *buildingZIPCode = [NSNumber numberWithInteger:10018];
NSArray* array = @[ [foo description], @"Another String", [bar description] ];
NSDictionary* dict = @{ NSForegroundColorAttributeName: [NSColor redColor] };
//wrong:
NSArray* array = @[[foo description], [bar description]];
NSDictionary* dict = @{ NSForegroundColorAttributeName :[NSColor redColor] };
如果集合所占代码多行,使用xcode默认的代码规范为:
NSArray* array = @[
@"This",
@"is",
@"an",
@"array"
];
NSDictionary* dictionary = @{
NSFontAttributeName : [NSFont fontWithName:@"Helvetica-Bold" size:12],
NSForegroundColorAttributeName : fontColor
};
============Naming============
命名主要参考Apple文档<Cocoa Coding Guidelines>,详见 《 iOS:Cocoa编码规范》。苹果命名规范文档<Cocoa Coding Guidelines>,主要是说明 Cocoa/写苹果框架时的命名规范,可能跟平时编写application-level code命名规范稍有差异。下面主要列出了有差异的地方,相同的不做说明:1.混合编码:许多的项目需要实现跨平台的C++代码,并与Objective-C以及Cocoa混合编写。或者以C++作为后端,Cocoa作为前端。这就导致了两种命名方式直接不统一。 我们的解决方案是:编码风格取决于方法/函数以哪种语言实现。如果在一个@implementation语句块中,就使用Objective-C的风格。如果实现一个C++的类,请使用C++的风格。这避免了成员变量与局部变量使用混合的命名风格,这会严重地影响可读性。
2.文件扩展名:.h--C/C++/Objective-C的头文件;.m--Ojbective-C实现文件 ;.mm--Ojbective-C++的实现文件;.cc--纯C++的实现文件;.c--纯C的实现文件。
3.关于前缀:application-level的代码,不同于某个cocoa框架,应该尽量避免不必要的前缀。为每个类都添加前缀不会提高任何的可读性。当设计跨不同应用程序的代码时,应该使用前缀,例如:GTMSendMessage。
总结:a.类名、分类名、协议名应该以大写字母开始,并混合小写字母来分隔单词(Camel命名法);
b.函数名不用以前缀开头,类似方法,小写字母开头,Camel命名法;
c.常数应该以小写字母k开头,使用混合大小写的格式来分隔单词,详见下文。
4.分类:如果我们要创建一个NSString的类别以解析时,我们将把类别放在一个名为 GTMNSString+Parsing.h的文件中。类别名本身的名字是GTMStringParsingAdditions(是的,我们知道类别名和文件名不一样,但是这个文件中可能存在多个不同的与解析有关类别)。类别中的方法应该以gtm_myCategoryMethodOnAString这种格式的前缀以避免命名冲突,因为Objective-C只有一个命名空间。如果代码不会被分享并且不会被运行在不同的地址空间中,方法名字就不那么重要。
类名与包含类别名的括号之间,应该以一个空格分隔。
总结:类别名应该有两三个字母的前缀以表示类别是项目的一部分或者该类别是通用的。类别应该包含它所扩展的类的名字。
// Extending a framework class:
@interface NSString (GTMStringParsingAdditions)
- (NSString *)gtm_foobarString;
@end
// Making your methods and properties private:
@interface FoobarViewController ()
@property(nonatomic, retain) NSView *dongleView;
- (void)performLayout;
@end
5.一般变量命名: 变量名使用小写开头的驼峰法,使变量名尽量可以推测其用途属性具有描述性。别一心想着少打几个字母,让你的代码可以迅速被理解更加重要。
// AVOID
int w;
int nerr;
int nCompConns;
tix = [[NSMutableArray alloc] init];
obj = [someObject object];
p = [network port];
// GOOD
int numErrors;
int numCompletedConnections;
tickets = [[NSMutableArray alloc] init];
userInfo = [someObject object];
port = [network port];
6.成员变量:之前google推荐以下划线作为后缀,如usernameTextField_。然而苹果综合考虑下划线作为前缀。
7.常量: 常量名(如宏、枚举、静态局部变量等)应该以小写字母k开头,使用混合大小写的格式来分隔单词,如:kInvalidHandle,kWritePerm。
const int kNumberOfFiles = 12;
NSString *const kUserKey = @"kUserKey";
enum DisplayTinge {
kDisplayTingeGreen = 1,
kDisplayTingeBlue = 2
};
============Comments============
虽然写起来很痛苦,但注释是保证代码可读性的关键。下面的规则给出了你应该什么时候、在哪进行注释。记住:尽量注释很重要,the best code is self-documenting。与其给类型及变量起一个晦涩难懂的名字,再为它写注释,不如直接起一个有意义的名字。当你写注释的时候,记得你在给你的听众写,即下一个需要阅读你的代码的代码贡献者。大方一点,下一个读代码的人可能就是你!
记住所有C++ Style Guide里的规则在这里也同样适用,不同的地方会在下面指出:
注意,个人注释风格:关于在代码上面一行,还是跟随代码后面注释的选择?
// 颜色 说明
#define BQSystemTextDarkGrayColor ColorFromRGB16HEX(0x6c6c6c) //暗灰色 :商品规格、配送时间、副标题等
#define BQSystemTextLightGrayColor ColorFromRGB16HEX(0xc35c6c) //商品市场价显示用到的浅灰色
#define BQSystemYellowColor ColorFromRGB16HEX(0xffcc00) //整个应用用到的黄色
说明:
如果是针对某一行的注释说明,一样注释跟随在代码后(另起一行,看起来不舒服),如果是针对下面一段说明,另起一行,写在前面。
--文件注释:
总结:以版权信息作为文件头部,开始每一个文件,后接文件内容的描述。
每个文件应该按顺序包括如下内容:
1.版权信息声明(如:Copyright 2008 Google Inc.)。选择一个合适的项目所使用的授权样板(例如,Apache 2.0, BSD, LGPL,GPL)。
2.文件内容的基本描述
如果你对其他人的原始代码作出重大的修改,请把你的名字添加到作者里面。当另外一个代码贡献者对文件有问题时,他需要知道怎么联系你,这十分有用。
--声明注释
总结:每个接口、类别以及协议应该注释,以描述它的目的及作用。
// A delegate for NSApplication to handle notifications about app
// launch and shutdown. Owned by the main app controller.
@interface MyAppDelegate : NSObject {
...
}
@end
如果你已经在文件头部详细描述了接口,可以直接说明“完整的描述请参见文件头部”,但是一定要有这部分注释。
另外, 公共接口的每个方法,都应该有注释来解释它的作用、参数、返回值以及其它影响。
我们之前都是假定文档是synchronization的,如果类的实例可以被多个线程访问,记得注释多线程条件下的使用规则。
--实现注释
总结:使用|来引用注释中的变量名及符号名而不是使用引号。
这会避免二义性,尤其是当符号是一个常用词汇,这使用语句读起来很糟糕。例如,对于符号"count":
// Sometimes we need |count| to be less than zero.
或者当引用已经包含引号的符号:
// Remember to call |StringWithoutSpaces("foo bar baz")|
--对象所有权
总结:Make the pointer ownership model as explicit as possible when it falls outside the most common Objective-C usage idiom。
继承自NSObject的成员变量指针,通常默认是强引用关系(retained)。如果它有没被类 retained,应该__weak说明或者注释。
当成员变量指向CoreFoundation、C++或者其它非Objective-C对象时,无论是强引用还是弱引用,都需要用__strong or __weak去说明。注意:Core Foundation 和非Objective-C对象指针require explicit memory management,即使是building for ARC or garbage collection。如果the __weak type modifier is not allowed (e.g. C++ member variables when compiled under clang),使用注释说明。