推荐文章:禅与 Objective-C 编程艺
前言
为高产品代码质量,指导广大软件开发人员编写出简洁、可维护、可靠、可测试、高效 、 可移植的代码,编程规范修订工作组分析 、总结了我司的各种典型编码问题,并参考了业界编程规 范近年来的成果,重新对我司1999年版编程规范进行了梳理、优化、刷新,编写了本规范。 本规范将分为完整版和精简版,完整版将包括更多的样例 、规范的解释以及参考材料(what &why),而精简版将只包含规则部分(what)以便查阅。在本规范的最后,列出了一些业界比较优秀的编程规范,作为延伸阅读参考材料。
代码总体原则
1、清晰第一清晰性是易于维护 、易于重构的程序必需具备的特征 。代码首先是给人读的,好的代码应当可以像文章一样发声朗诵出来。目前软件维护期成本占整个生命周期成本的40%~90%。根据业界经验,维护期变更代码的成本,小型系统是开发期的5倍,大型系统( 100万行代码以上)可以达到100倍。
业界的调查指出,开发组平均大约一半的人力用于弥补过去的错误,而不是添加新的功能来帮助公司高竞争力。
“程序必须为阅读它的人而编写,只是顺便用于机器执行。”——HaroldAbelson和Gerald Jay Sussman
“编写程序应该以人为本,计算机第二。”——Steve McConnell
本规范通过后文中的原则(如头优秀的代码可以自我解释,不通过注释即可轻易读懂/头文 件中适合放置接口的声明,不适合放置实现/除了常见的通用缩写以外,不使用单词缩写,不得使 用汉语拼音)、规则(如防止局部变量与全局变量同名)等说明清晰的重要性。
一般情况下,代码的可阅读性高于性能,只有确定性能是瓶颈时,才应该主动优化。
2、简洁为美简洁就是易于理解并且易于实现。代码越长越难以看懂,也就越容易在修改时引入错误 。写 的代码越多,意味着出错的地方越多,也就意味着代码的可靠性越低 。因此,我们倡大家通过编 写简洁明了的代码来升代码可靠性。
废弃的代码(没有被调用的函数和全局变量)要及时清除,重复代码应该尽可能炼成函数 。
3、选择合适的风格,与代码原有风格保持一致产品所有人共同分享同一种风格所带来的好处,远远超出为了统一而付出的
代价 。在公司已 有编码规范的指导下,审慎地编排代码以使代码尽可能清晰,是一项非常重要的技能。如果重构/修改其他风格的代码时,比较明智的做法是根据现有代码的现有风格继续编写代码 。
规范实施、解释
本规范制定了编写iOS程序的基本原则、规则和建议。本规范适用于公司内所有iOS软件。本规范自发布之日起生效,对以后新编写的和修改的代码应遵守本规范。本规范由质量体系发布和维护。实施中遇到问题,可以 在组内出。 个体程序员不得违反本规范中的相关规则。
术语定义
头文件
-
#import引用Objective-C/Objective-C++头文件;#include引用C/C++头文件;使用#include时要注意#define头保护。
-
#import头文件按模块分类。
说明:对头文件按模块分类,使代码易读,也便于理解。 -
#import的顺序,应该是先引用自定义类,再引用系统类/框架。
说明:将系统类放在最后引入,可避免和检验自定义类的头文件引用不完整 漏洞。 -
#import根框架而不是单独的零散文件
说明:当你试图从框架(如Cocoa或者Foundation)中包含若干零散的系统头文件时,实际上包 含顶层根框架的话,编译器要做的工作更少。根框架通常已经经过预编译,加载更快。 -
@class与#import
说明:如需要继承类或执行协议,可以在.h中进行#import类或协议;其他情况下,在.h中声明用@classs声明此类即可。这样可以减少因头文件依赖引起重复编译,高编译速度。
常量&变量
-
init和dealloc内避免使用访问器
说明:在init和dealloc方法执行的过程中,子类可能会处在一个不一致的状态,所以这些方 法中的代码应避免调用访问器。子类尚未初始化,或在init和dealloc方法执行时已经被销毁,会使访问器方法很可能不可靠。 实际上,应在这些方法中直接对 内部成员变量进行赋值或释放操作。 -
禁止对属性做出错误的内部成员变量声明
说明:xcode4.4开始会自动@synthesize ,为@ property声明一个前缀下划线的内部成员变量。因 此,不需要为属性重复做出内部成员变量声明。同时,禁止 对属性做出错误的内部成员变量声明 。 -
使用常类型变量,而不是内嵌的字符串字面值或数字
说明:常类型变量便于复用常用的变量值(如π),同时可以快速地修改值而无需查找替换。 -
不用在init方法中,将成员变量初始化为0或者nil
说明:刚分配的对象,默认值都是0,除了isa指针(译者注: NSObject的isa识对象的类型)。所以不用在初始化器里面写一堆将成员初始化为0或者nil的代码。 -
按实际需要来定义成员变量的作用域
说明:
1、如果只是单纯的private变量,最好声明在interface里。
2、如果是类的public属性,就用property写在.h文件里。
3、如果自己内部需要setter和getter来实现一些东西,就在.m文件的类目里用property来声明。 不要将私有的实例变量和方法声明在头文件中,应将私有变量和方法声明在实现文件的类扩展内 。 -
字符串应使用copy属性(Attribute)
说明:应该用copy属性(attribute)声明NSString属性(property)。 从逻辑上,确保遵守NSString的setter必须使用copy而不是retain的原则。 -
谨慎声明属性的原子性
说明:一定要注意属性(property)的开销。缺省情况下,所有synthesize的setter和getter都是原子的。这会给每个get或者set带来一定的同步开销。将属性( property )声明为nonatomic,除非你需要原子性。 -
点引用只用于简单的属性set、get操作
说明:点引用只用于简单的属性set、get操作,但不应该用它来调用对象的其它操作。 -
autorelease优先,retain其次(非ARC)
说明:当给一个变量赋值新的对象时,必须先释放掉旧的对象以避免内存泄露。有很多“正确的”方法可以处理这种情况。我们则选择“autorelease之后retain”的方法,因为事实证明它不 容易出错。注意大的循环会填满autorelease池,并且可能效率上会差一点,但权衡之下我们认为是可以接受的。 -
使用nil来检查应用程序的逻辑流程,而不是避免崩溃
说明:Objective-C运行时会处理向nil对象发送消息的情况。如果方法没有返回值,就没关系。 如果有返回值,可能由于运行时架构、返回值类型以及OS X版本的不同而不同,参见Apple’s documentation。注意,这和C/C++中检查指针是否为’NULL’很不一样,C/C++运行时不做任何检查,从而导致 应用程序崩溃。因此你仍然需要保证你不会对一个C/C++的空指针解引用。 -
BOOL的陷阱,不要直接将BOOL值与YES进行比较
说明:Ojbective-C中把BOOL定义成无符号字符型,这意味着BOOL类型的值远不止YES(1)或NO(0),所以,不要直接将BOOL值与YES进行比较。
//示例-1:
BOOL a = 2.5;
if (a == YES) {
NSLog(@"a == yes"); }
else if(a == NO) {
NSLog(@"a == no");
} else {
NSLog(@"a == other");
}
NSLog(@"a+0.5 = %f",a+0.5);
iOS编程规范v1.0
==========2014-04-07 21:56:14.280 2014-04-07 21:56:14.281 :test[1224:907] a == other test[1224:907] a+0.5 = 2.500000
//示例-2:
BOOL a =130;
if (a == YES) {
NSLog(@"a == yes"); }
else if(a == NO) {
NSLog(@"a == no");
} else {
NSLog(@"a == other");
}
NSLog(@"a=%hhd a+0.5 = %f",a,a+0.5);
==========2014-04-07 22:14:07.986:test[1510:907] a == other2014-04-07 22:14:07.988 test[1510:907] a=-126 a+0.5 =-125.500000
- BOOL的陷阱,不要直接把整形转换成BOOL
说明:Ojbective-C中把BOOL定义成无符号字符型,不要直接把整形转换成BOOL。常见的错误 包括将数组的大小、指针值及位运算的结果直接转换成BOOL ,取决于整型结果的最后一个字节,很可能会产生一个NO值。当转换整形至BOOL时,使用三目操作符来返回YES或者NO。(译者 注:读者可以试一下任意的256的整数的转换结果,如256、512 ...)
例:
//示例:错误的用法:
-(BOOL)isBold {
return [self fontTraits] & NSFontBoldTrait;
}
-(BOOL)isValid {
return [self stringValue];
}
//正确的用法:
-(BOOL)isBold {
return ([self fontTraits] & NSFontBoldTrait) ?YES : NO;
}
-(BOOL)isValid {return [self stringValue] != nil;} - (BOOL)isEnabled {
return [self isValid] && [self isBold];
}
- 尽量使用NSInteger,而不是int
说明:当需要使用int类型的变量的时候,可以像写C的程序一样,用int,也可以用NSInteger,但 更推荐使用NSInteger,因为这样就不用考虑设备是32位的还是64位的.
-点标记语法
说明:属性和幂等方法(多次调用和一次调用返回的结果相同)使用点标记语法访问,其他的情况 使用方括号标记语法。
示例:
//良好的风格:
view.backgroundColor = [UIColor orangeColor];
[UIApplication sharedApplication].delegate;
//不良的风格:
[view setBackgroundColor:[UIColor orangeColor]]; UIApplication.sharedApplication.delegate;
类和方法
-
当子类需要使用init函数时,确保已经重载了父类的构造函数。
说明:当子类需要使用init函数时,确保已经重载了父类的构造函数 。否则,子类的初始化不会调 用,这会导致很多很难定位的bug。 -
出参与入参需对应
说明:在32位系统中,int和long都是32位的,但是在64位系统中,long则是64位的。将long赋值给int,在64位系统中,会出现truncate的隐患。 -
明确指定构造函数
说明:对于需要继承你的类的人来说,明确指定构造函数十分重要 。这样他们就可以只重写一个构 造函数(可能是几个)来保证他们的子类的构造函数会被调用 。这也有助于将来别人调试你的类时,理解初始化代码的工作流程。 -
避免重载+new
说明:不要调用NSObject的类方法new,或在子类中重载new方法。使用new方法会使关于内存分配 的代码审核变得很困难。使用alloc和init来创建并初始化对象。 -
保持公共API简单
说明:保持类简单;避免“厨房水槽( kitchen-sink)”式的API。如果一个函数压根没必要公开,就不要这么做。用私有类别保证公共头文件整洁。与C++不同, Objective-C没有方法来区分公共的方法和私有的方法–所有的方法都是公共的(译者注:这取决于Objective-C运行时的方法调用的消息机制)。因此,除非客户端的代码期望 使用某个方法,不要把这个方法放进公共API中。尽可能的避免了你不希望被调用的方法却被调 用到。这包括重载父类的方法 。对于内部实现所需要的方法,在实现的文件中定义一个类别,而不 是把它们放进公有的头文件中。 -
大的@implementation用categories拆分成更容易理解的小块。
说明:便于理解的同时,还可以为最适合的类添加新的、特定应用程序的功能。
例如,当添加一个“middle truncation”方法时,创建一个NSString的新类别并把方法放在里面,要比创建任意 的一个新类把方法放进里面好得多。
宏
- 用宏定义表达式时,要使用完备的括号。
示例:
如下定义的宏都存在一定的风险。
#define RECTANGLE_AREA( a, b ) a * b
#define RECTANGLE_AREA( a, b ) (a * b)
#define RECTANGLE_AREA( a, b ) (a) * (b)
正确的定义应为:
#define RECTANGLE_AREA( a, b ) ((a) * (b))
- 将宏所定义的多条表达式放在大括号中。
示例:下面的语句只有宏的第一条表达式被执行。为了说明问题, for语句的书写稍 不符规范。
#define INTI_RECT_VALUE( a, b )\a = 0;\b = 0;for (index = 0; index <
RECT_TOTAL_NUM; index++) INTI_RECT_VALUE( rect.a, rect.b );
正确的用法应为:
#define INTI_RECT_VALUE( a, b )\ {\a = 0;\b = 0;\}
- 使用宏时,不允许参数发生变化。
示例:
如下用法可能导致错误。
#define SQUARE( a ) ((a) * (a))int a = 5;int b;b = SQUARE( a++ ); //结果:a = 7,即执行了两次增1。
正确的用法是:
b = SQUARE( a );a++; //结果:a = 6,即只执行了一次增1。
其他
-
委托模式
说明:
委托对象不应该被retain实现委托模式的类应:
1、拥有一个名为_delegate的实例变量来引用委托。
2、因此,访问器方法应该命名为delegate和setDelegate:。
3、_delegate对象不应该被retain。 -
模型/视图/控制器(MVC)
说明:分离模型与视图。分离控制器与视图、模型。
1、分离模型与视图:
不要假设模型或者数据源的表示方法 。保持数据源与表示层之间的接口抽象。视图不需要了解模型的逻辑(主要的规则是问问你自己,对于数据源的一个实例,有没有可能有多种不同状态的表示方法)。
2、分离控制器与模型、视图:
不要把所有的“业务逻辑”放进跟视图有关的类中。这使代码非常难以复用 。使用控制器类来处理这些代码,但保证控制器不需要了解太多表示层的 逻辑。 -
应该使用线程安全的模式创建共享的单例实例
示例:
+(instancetype)sharedInstance {
static id sharedInstance = nil;
static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
-
尽量使用不透明视图
说明:不透明的视图可以极大地高渲染的速度。因此如非必要,可以将table cell及其子视图的opaque属性设为YES(默认值)。 -
做条件判断时,应使用Golden Path模式
说明:The goal here is to make the code on the left margin to be the “expected” code execution path and the code that is indented to be the exception. Consistency in this area is important to code readability.
示例:
//良好的风格:
-(void)someMethod{
if (![someOther boolValue])
return;
//Do something important
}
//不良好的风格:
-(void)someMethod{
if ([someOther boolValue]) {
//Do something important
}
}
================================
//良好的风格:
-(void)someMethod{
if ([someOther boolValue]) {
//Do something important
return;
}
//Do something else important
}
//不良好的风格:
-(void)someMethod{
if ([someOther boolValue]) {
//Do something important
} else {
//Do something else important
}
}
================================
//例外:
-(void)someMethod{
if ([someOther boolValue]) {
//Do something important
} else {
//Do something else important
}
//Do this no matter what
}
- 异常和错误处理
不要在流控制语句中使用异常( NSException )。异常仅用于表明程序员的错误。为了表明一个错误,使用NSError *。 当一个方法通过引用返回一个错误参数,应该检测返回值的状态,而不是错误参数的状态。
//良好的风格:
NSError *error;
if (![self trySomethingWithError:&error]) {
// Handle Error
}
//不良的风格:
NSError *error;
[self trySomethingWithError:&error];
if (error) {
// Handle Error
}
//在方法执行成功的情况下赋值非Null值给错误参数,会使路径跳转到假条件分支(随后程序奔溃)
- 三元运算符
说明:
长的三元运算符应使用圆括号括起来。三元运算符仅用于赋值和做参数。Blah *a = (stuff == thing ? foo : bar);
合并的nil三元运算符应该尽量避免。
示例:
//不良的风格:
Blah *b = thingThatCouldBeNil ?: defaultValue;
//多分支条件应该使用if语句或重构为实例变量。
// 良好的风格:
result = a > b ? x : y;
///不良的风格:
result = a > b ? x = c > d ? c : d : y;
命名
-
关于命名和命名风格
说明:
对于易维护的代码而言,命名规则非常重要。Objective-C的方法名往往十分长,但代码块 读起来就像散文一样,不需要太多的代码注释。
当编写纯粹的Objective-C代码时,我们基本遵守标准的Objective-C namingrules,这 些命名规则可能与C++风格指南中的大相径庭。例如, Google的C++风格指南中推荐使用下划线分隔的单词作为变量名,而(苹果的)风格指南则使用驼峰命名法,这在Objective-C社区中非 常普遍。
命名可适当使用缩略词。但必须确保,这些缩略词是久经历史,认识性比较高的缩略词。如 下:缩略词。不要使用,你自己认为大家可能会理解的缩略词 。因为,跳出语言环境之后,这个缩 略词就变得难懂了。
当编写Objective-C++代码时,事情就不这么简单了。许多项目需要实现跨平台的C++ API,并混合一些Objective-C、Cocoa代码,或者直接以C++为后端,前端用本地Cocoa代码。 这就导致了两种命名方式直接不统一。我们的解决方案是:编码风格取决于方法/函数以哪种语言 实现。如果在一个@implementation语句中,就使用Objective-C的风格。如果实现一个C++的类,就使用C++的风格。这样避免了一个函数里面实例变量和局部变量命名规则混乱,严重影响 可读性。 -
命名尽量简洁,但不能因为简洁而使命名难以理解
示例:
//Code
insertObject: atIndex: insert:at: removeObjectAtIndex: removeObject:remove:In general, don’t abbreviate names of things.
/***/
//Code
destinationSelection
destSel
setBackgroundColor:
setBkgdColor:
常量和变量
- 关于变量名
说明:
变量名应该以小写字母开头,并使用驼峰格式 。尽量为变量起一个述
性的名字 。不要担心 浪费列宽,因为让新的代码阅读者立即理解你的代码更重要。
示例:
//错误的命名:
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
- 不要使用单个字符来定义变量名。
说明:
即时变量只是一个index,为它取名index,而不是i、j、k。 一般循环语句的当前对象的命名前缀包括“ one ”、“ a/an ”。对于简单的单个对象使用“ item ”命名。
示例:
//良好的风格:
for (i = 0; i < count; i++) {
oneObject = [ allObjects objectAtIndex: i];
NSLog (@"oneObject: %@", oneObject);
}
NSEnumerator *e = [allObjects objectEnumerator];
id item;
while (item = [e nextObject])
NSLog (@"item: %@", item);
- 对于NSString、NSArray、NSNumber或BOOL类型,变量的命名一般不需要表明其 类型。
示例:
//良好的风格:
NSString *accountName;
NSMutableArray *mailboxes;
NSArray *defaultHeaders;
BOOL userInputWasUpdated;
//不良的风格:
NSString *accountNameString ;
NSMutableArray *mailbox Array ;
NSArray *defaultHeaders Array ;
BOOL userInputWasUpdated BOOL ;
//如果变量不是以上基本常用类型,则变量的命名就应该反映出自身的类型 。但有时仅需要某 些类的一个实例的情况下,那么只需要基于类名进行命名。
NSImage *previewPaneImage ;
NSProgressIndicator *uploadIndicator ;
NSFontManager * fontManager ;
//基于类名命名
//大部分情况下, NSArray或NSSet类型的变量只需要使用单词复数形式(比如mailboxes ),不必在命名中包含“ mutable ”。如果复数变量不是NSArray或NSSet类型,则 需要指定其类型。
//良好的风格:
NSDictionary * keyedAccountNames;
NSDictionary * messageDictionary ;
NSIndexSet * selectedMailboxesIndexSet ;
类和方法
-
不要使用下划线前缀来声明私有方法。
说明:苹果保留这一风格。 -
方法的每个参数必须有参数声明,尽可能对参数做出述,不能为空
示例:
//正确
-(void)sendAction:(SEL)aSelectortoObject:(id)anObject forAllCells:(BOOL)flag;
//错误
-(void)sendAction:(SEL)aSelector :(id)anObject :(BOOL)flag;
//正确
- (id)viewWithTag:(NSInteger)aTag;
//错误
- (id)taggedView:(int)aTag;
关于类名
-
类名(以及类别、协议名)应首字母大写,并以驼峰格式分割单词。应用层的代码,应该尽 量避免不必要的前缀 。为每个类都添加相同的前缀无助于可读性 。当编写的代码期望在不同应用程 序间复用时,应使用前缀(如:GTMSendMessage )。
-
关于类别名
说明:类别名应该有两三个字母的前缀以表示类别是项目的一部分或者该类别是通用的 。类别名应 该包含它所扩展的类的名字。
比如我们要基于NSString创建一个用于解析的类别,我们将把类别放在一个名为GTMNSString+Parsing.h的文件中。类别本身命名为GTMStringParsingAdditions (是的,我们 知道类别名和文件名不一样,但是这个文件中可能存在多个不同的与解析有关类别)。类别中的方 法应该以gtm_myCategoryMethodOnAString:为前缀以避免命名冲突,因为Objective-C只有一 个名字空间 。如果代码不会分享出去,也不会运行在不同的地址空间中,方法名字就不那么重要了 。类名与包含类别名的括号之间,应该以一个空格分隔。 -
关于方法名
说明:一个方法的命名首先述返回什么,接着是什么情况下被返回 。方法前面中冒号的前面述传入参数的类型。 以下类方法和实例方法命名的格式语法:
[object/class thing+ condition ];
[object/class thing+ input :input];
[object/class thing+identifer :input];
方法名应该以小写字母开头,并混合驼峰格式。每个具名参数也应该以小写字母开头。 方法名应尽量读起来就像句子,这表示你应该选择与方法名连在一起读起来通顺的参数名。(例如,convertPoint:fromRect:或replaceCharactersInRange:withString:)。
详情参见Apple’s Guide to Naming Methods。访问器方法应该与他们 要获取的成员变量的名字一样,但不应该以get作为前缀。
例如:
-(id)getDelegate; // AVOID
-(id)delegate; // GOOD示例:
rate= [number floatValue];
newString= [string posedStringWithCanonicalMapping ];
subarray=[array arrayWithRange :segment];
decom = [path stringByExpandingTildeInPath ];
string = [string ByAppendingString :@"Extra Text"];
sub = [array objectAtIndex :3];
string = [NSString WithFormat :@"%f",1.5];
array = [NSArray WithObject :newString];
//不良的风格:
-sortInfo //是返回排序结果还是给info做排序-refreshTimer
//返回一个用于刷新的定时器还是刷新定时器
-update
//更新什么,如何更新
//良好的风格:
-currentSortInfo // "current"清楚地修饰了名词SortInfo
-refreshDefaultTimer // refresh是一个动词。-updateMenuItemTitle //一个正在发生的动作
这仅限于Objective-C的方法名。C++的方法与函数的命名规则应该遵从C++风格指南中 的规则。
- 关于方法的参数名
说明:
方法参数名一般使用的前缀包括“ the ”、“ an ”、“ new ”。
示例:
//良好的风格:
-(void)setTitle:(NSString*)aTitle;
//Cocoa命名举例:
realPath
fullString
object
//类方法newStringnewArray良好的自定义方法命名风格:recipients = [emailrecipients SortedByLastName ];
newEmail = [CDCEmail emailWithSubjectLine :@"Extra Text"];
emails = [mailbox messagesReceivedAfterDate :yesterdayDate];
当需要获取对象值的另一种类型的时候,方法命名的格式语法如下: [object adjective +thing];
[object adjective +thing+ condition ];
[object adjective +thing+input :input];
//良好的自定义方法命名风格:
capitalized = [name capitalized String];
-(void) setName:
-(id) keyForOption:
-(NSArray *) emailsForMailbox:
-(CDCEmail *) emailForRecipients: (NSArray *) theRecipients;
-(NSString *) newName; (CDCOption *) anOption (CDCMailbox *) theMailbox;
-
方法的参数名不要使用“and”做链接。
说明:当参数较多时,代码会显得冗余。有一个例外,当参数述的是两个不同的action时,可以 用“and”做链接。 -
框架类模块中,在类名和常类型变量名前添加一个由三个大写的字母组成的前缀(如RNC )。
说明:
由于Objective-C不支持名字空间,为了防止出现命名空间的冲突,在类名和常类型变量 名前添加一个由三个大写的字母组成的前缀(如RNC ),对于Core Data实体名则可以忽略此规 则。如果你子类化了标准的Cocoa类,将前缀和父类名合并是一个很好的做法。如继承UITableView的类可命名为RNCTableView。
其他
关于文件名
说明:
文件名须反映出其实现了什么类–包括大小写。遵循你所参与项目的约定。 文件的扩展名应该如下:
类别的文件名应该包含被扩展的类名,如: GTMNSString+Utils.h或GTMNSTextView+Autocomplete.h
。
-
关于Objective-C++
说明:源代码文件内,Ojbective-C++代码遵循你正在实现的函数/方法的风格。为了最小化Cocoa/Objective-C与C++之间命名风格的冲突,根据待实现的函数/方法选 择编码风格。实现@implementation语句块时,使用Objective-C的命名规则;
如果实现一个C++
.h ===C/C++/Objective-C的头文件
.m ===Ojbective-C实现文件
.mm ===Ojbective-C++的实现文件
.cc ===纯C++的实现文件
.c ===纯C的实现文件的类,就使用C++命名规则。 -
图片资源的命名
说明:图片的命名应该保持一致,以图片的用途述作为图片文件名 。文件名的命名使用驼峰式大 小写风格,文件名后可跟随一个自定义的类名或者是自定义的属性名(如果有属性名)、也可以 再跟上颜色述以及/或者位置、图片的最终状态。
示例:
良好的风格:
RefreshBarButtonItem / RefreshBarButtonItem@2x和
RefreshBarButtonItemSelected / RefreshBarButtonItemSelected@2x
ArticleNavigationBarWhite / ArticleNavigationBarWhite@2x和ArticleNavigationBarBlackSelected / ArticleNavigationBarBlackSelected@2x.
被用作相似用途的图片应该使用一个图片文件夹进行分开管理。
- 关于缩略词1
说明:
虽然方法命名不应使用缩略词,然而有些缩略词在过去被反复的使用,所以使用这些缩略词 能更好的的表达代码的含义。
缩略词 | 含义和备注 |
---|---|
alloc | 分配,拨出 |
alt | 轮流,交替 |
app | 应用程序。比如NSApp表示全局程序对象 |
calc | 计算 |
dealloc | 销毁、析构 |
func | 函数 |
horiz | 水平的 |
info | 信息 |
init | 初始化 |
max | 最大的 |
min | 最小的 |
msg | 消息 |
nib | Interface Builder文档 |
pboard | 黏贴板(仅对常量) |
rect | 矩形 |
temp | 临时、暂时 |
vert | 垂直的 |
以下是一些常用的首字母缩略词ASCIIPDFXML:
HTML
URL
RTF
HTTP
TIFF
JPG
PNG
GIF
LZW
ROM
RGB
CMYK
MIDI
FTP
注释
-
文件/类/框架的注释尽量完整。变量/方法的注释尽量简洁。
说明:
尽管注释很重要,但最好的代码应该自成文档。 并且注释的更新往往没有代码更新及时 。与 其写一段复杂的注释,不如直接起一个有意义的名字 。
更新代码时一定要更新注释,防止对代码造 成误解。 -
文件注释
说明:
每个文件的开头以文件内容的简要述起始,紧接着是作者,最后是版权声明和 样板。版权信息及作者每个文件应该按顺序包括如下项:
1、许可证
2、文件内容的简要述
3、代码作者
4、版权信息声明(如:Copyright 2008 GoogleInc.)
5、必要的话,加上许可证样板。为项目选择一个合适的授权样板(例如,Apache 2.0, BSD, LGPL,GPL)。如果你对其他人的原始代码作出重大的修改,请把你自己的名字添加到作者里面 。当另外一个代码 贡献者对文件有问题时,他需要知道怎么联系你,这十分有用。 -
使用javadoc-style注释风格。
说明:
注释的第一行是对注释API的总结,随后的注释行是对代码更多细节的解释。
示例:
良好的风格:
/**
\* The maximum size of a download that is allowed.
\* If a response reports acontent length greater than the max will be \* cancelled.
\*This is helpful for preventing excessive memory usage.
\* Setting this to zero will allow all downloads regardless of size.
\* @default 150000 bytes
**/
@property (nonatomic) NSUInteger maxContentLength;
- 声明部分的注释
说明:
每个接口、类别以及协议应辅以注释,以述它的目的及与整个项目的关系。如果你已经在文件头部详细述了接口,可以直接说明“完整的述请参见文件头部”,但是一定 要有这部分注释。 另外,公共接口的每个方法,都应该有注释来解释它的作用、参数、返回值以及其它影响。 如果有的话,为类的线程安全性作注释。如果类的实例可以被多个线程访问,记得注释多线程条件下的使用规则。
// 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")|
-
程序中变量、方法命名尽量能以字面意思表示功能,对于需要用注释来解释的部分代码,注释以如下格式表述:
示例:/**
方法或变量说明 @param参数1说明(针对方法)* @param参数2说明(针对方法)* @return若方法有返回值则对返回值作说明*/ -
每一个方法之前都有一个99字符宽的注释行,注释行相对于使用空行更能高代码的 辨识度,当一行代码很长的时候,注释行也起到了越界检测的作用。
注释行: ///////////////////////////////////////////////////////////////////////////////////// //////////////
格式
- 尽量让你的代码保持在100列之内。
说明:我们深知Objective-C是一门繁冗的语言,在某些情况下略超100列可能有助于高可读 性,但这也只能是特例而已,不能成为开脱 。对于超长的代码,我们要怀疑代码逻辑的简练性。 设置方法: Xcode > Preferences > Text
Editing > Show page guide。
空格
-指针变量的星号指示符应该紧靠变量。
示例:
NSString *text
,而不是NSString* text
或NSString * text
。
-方法声明中,-/ +和返回类型之间须使用一个空格。
示例:
-(void)doSomethingWithString:(NSString *)theString { ... }
- 运算符间距1
说明:
二元运算符和参数之间需要放置一个空格,一元运算符、强制类型转换和参数之间不放置空 格。关键字之后圆括号之前,需要放置一个空格。示例:
void *ptr = &value + 10 * 3; NewType a = (NewType) b;for ( int i = 0; i < 10; i ++ ) { doCoolThings();
}
- 运算符间距2
说明:
数组和字典类型的字面值的 方括号两边 各放置一个空格。
NSArray *theShit = @[ @1 , @2, @3 ] ;
字典字面值的键和冒号之间没有空格,冒号和值之间有一个空格。
NSDictionary *keyedShit = @{ GHDidCreateStyleGuide: @YES };
C函数声明中,左括号的前面 不保留空格,并且函数名应该像类一样带有命名空间标识。
良好的风格:
void RNCwesomeFunctio n( BOOL hasSomeArgs);
长的字面值应被拆分为多行。
良好的风格:
NSArray *theShit = @[
@"Got some long string objects in here.", [AndSomeModelObjects too],@"Moar strings."];
NSDictionary *keyedShit = @{@"this.key": @"corresponds to this value",
@"otherKey": @"remoteData.payload", @"some": @"more",@"JSON":
@"keys",@"and": @"stuff",
};
-
只使用空格,不使用制表符。一次缩进两个空格。
说明:
我们使用空格缩进,不在代码中使用制表符。在跨平台开发中,不同IDE的tab对应缩进格式 不同。应该将编辑器设置成自动将制表符替换成空格。 -
类型标识符和尖括号内的协议名之间,不能有任何空格。
这条建议适用于类声明、实例变量以及方法声明。
示例:
@interface MyProtocoledClass : NSObject {
@private
id delegate_;
}
-(void)setDelegate:(id)aDelegate;
@end
- @public和@private访问修饰符应该以一个空格缩进。
示例:
@interface MyClass : NSObject {
@public ...
@private ...
}
@end
- 异常捕获中,每个@标签应该有独立的一行,在@与{}之间需要有一个
空格,@catch与被 捕捉到的异常对象的声明之间也要有一个空格。
如果你决定使用Objective-C的异常,那么就按下面的格式。不过你最好先看看 避免抛出异常 了 解下为什么不要使用异常。
@try {
foo();
}
@catch (NSException *ex) {
bar(ex);
}@finally {
baz();
}
-
重载NSObject的方法
如果重载了NSObject类的方法,强烈建议把它们放在@implementation内的起始处,这也是常 见的操作方法。通常适用(但不局限)于init..., copyWithZone:,以及dealloc方法。所有init...方法应 该放在一起, copyWithZone:紧随其后,最后才是dealloc方法。 -
Delegates和protocol对象声明在ivar的后面。
说明:delegates的命名往往比较长。 -
属性声明和定义的位置
说明:
属性的声明必须紧靠着类接口中的实例变量语句块。属性的定义必须在@implementation的 类定义的最上方。他们的缩进与包含他们的@interface以及@implementation语句一样。
示例:
@interface MyClass : NSObject {
@private
NSString *name_;
}
@property(copy, nonatomic) NSString *name; @end@implementation MyClass @synthesize name = name_;
- (id)init { ...}
@end
- 当需要实现多个协议的时候,将每一个协议名拆分到单独的行。
示例:良好的风格:
@interface CustomModelViewController : TTViewController <
TTModelDelegate,TTURLRequestDelegate>
- 关于方法参数与调用
说明:
方法有非常多的参数,应将每个参数单独拆成一行,且每个参数前的冒号对齐。
示例:
-(void)doSomethingWith:(GTMFoo *)theFoo
rect:(NSRect)theRect
interval:(float)theInterval {
... }
说明:当第一个关键字比其它的短时,保证下一行至少有 直对齐,而不是使用冒号对齐
-
关于块(闭包)
说明:
块(block)适合用在target/selector模式下创建回调方法时,因为它使代码更易读。块 中的代码应该缩进4个空格。取决于块的长度,下列都是合理的
风格准则:
1、如果一行可以写完块,则没必要换行。
2、如果不得不换行,关括号应与块声明的第一个字符对齐。
3、块内的代码须按4空格缩进。
4、如果块太长,比如超过20行,建议把它定义成一个局部变量,然后再使用该变量
5、 如果块不带参数,^{之间无须空格。如果带有参数, ^(之间无须空格,但) {之间须有一个
空格。
6、块内允许按两个空格缩进,但前是和项目的其它代码保持一致的缩进风格。 -
左右花括号
说明:
方法签名以及其他关键字( if/else/switch/wh ile等)后面跟随的左花括号总是和语句出 现于同一行,而右花括号独占一行。
示例:
良好的风格:
if (user.isHappy) {
//Do something
}else {
//Do something else
}
如果一个方法内有多个功能区域,可以使用空行分隔功能区域。
- 条件体内的逻辑块,需用花括号包围
说明:
即使条件体只需编写一行代码也必须使用花括号。
良好的风格做法:
if(!error) {
return success;
}
//不良的风格:
if (!error)
return success;
//或:
if (!error) return success;
- 内部成员变量按逻辑分组.
说明:便于理解这个类。
网友评论