1:类别命名
类别命名应该以两三个字符的分类前缀作为一个项目或通用的公用部分。类别名应该包含类的扩展。举个例子,如果我们想要创建一个基于NSString的类别用于解析,我们应该把类别放到名字是LMNSString+Parsing.h的文件里,而类别本身的名字则是LMStringParsingAdditions(是的,我们明白这个类别和其文件名字不匹配,但这个文件可能还包括其他用于解析相关的类别)。类别的方法应该都使用一个前缀(型如lm_myCategoryMethodOnAString),以防止Objective-C代码在命名空间里冲突。如果代码本来就不考虑共享或在不同的地址空间(address-space),方法命名规则就没必要恪守了。
2:类命名
类名(不包括类别和协议名)应该用大写开头的驼峰命名法。在应用级别的代码里,尽量不要使用带前缀的类名。每个类都有相同的前缀不能提高可读性。不过如果是编写多个应用间的共享代码,前缀就是可接受并推荐的做法了(型如LMSendMessage)。
3:#import和#include
用#import导入Objective-C或Objective-C++头文件,用#include导入C或C++头文件。标准C和C++里根本没有#import,所以也只能用#include导入。
4:使用根框架
导入框架根的头文件而不是分别导入框架头文件,看起来从Cocoa或Foundation这些框架里导入个别的文件很不错,但实际上你直接导入框架根头文件效率更高。框架根已经被预编译故可更快的被加载。还有,记住用#import指令而不是#include导入Objective-C的框架。
#import <Foundation/Foundation.h> // good
#import <Foundation/NSArray.h> // avoid
#import <Foundation/NSString.h>
...
5:行长度
代码中的每行文本不要超过80个字符的长度。你可以在Xcode里清楚地发现代码中的违规,设置Xcode>Preferences>TextEditing>Showpageguide(之后就可以在代码编辑区域里看到一条指定字符长度的指示线了)。
6:@public和@private
权限控制符@public和@private缩进一个空格:
@interface MyClass : NSObject {
@public
...
@private
...
}
@end
7:常量
常量(预定义,枚举,局部常量等)使用小写c开头的驼峰法,比如cInvalidHandle,cWritePerm。同理对于枚举变量可以使用e开头的驼峰写法。
8:Objective-C方法命名
方法使用小写开头的驼峰法命名,每个参数都应该小写开头。方法名应该尽可能读起来像一句话,参数名就相对方法名的补充说明(比如convertPoing:fromRect:或者replaceCharactersInRange:withString:)。存取(Accessor)方法应该一致的在"取(getting)"的时候直接用变量名而不是在签名加"get",如下:
- (id)getDelegate; // AVOID
- (id)delegate; // GOOD
不过这仅针对Objective-C代码,C++代码仍然遵循自己的代码规范。
9:匈牙利命名法
是一种编程时的命名规范。基本原则是:变量名=属性+类型+对象描述,其中每一对象的名称都要求有明确含义,可以取对象名字全称或名字的一部分。
10:驼峰命名法
就是当变量名或函式名是由一个或多个单字连结在一起,而构成的唯一识别字时,驼峰命名法第一个单字以小写字母开始;第二个单字的首字母大写或每一个单字的首字母都采用大写字母。
11:方法声明与定义
留一个空格在-或+和返回类型之间,但参数列表里的参数之间不要留间隔。参数对象的星号前需要加空格。方法应该写成这样:
- (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];
12:保持公有API简明
保持你的类简单,如果一个方法没必要公开就不要公开。使用私有类别保证公开头文件的简洁。和C++不同,Objective-C无法区分公有私有方法,因为它全是公有的。
13:NSString的属性定义为copy
如果自己实现setters方法,也请使用copy而不是retain。在NSString上调用Setters方法时,永远使用copy方式。永远不要retain一个字符串,这可以防止调用者在你不知到的情况下修改了字符串。不要以为你可以改变NSString的值,只有NSMutableString才能做到。
14:避免调用new方法
不要调用NSObject 的new方法,也不要在子类中重写它,而是应该使用 alloc 和 init 方法来初始化retained的对象。
Objective-C代码显式调用 alloc 和 init 方法来创建和retain一个对象。new 的方法可能会带来内存上调试的麻烦。
15:初始化变量
没必要在初始化方法里把变量初始化为0或者nil,这是多余的。所有新分配内存的对象内容都初始化为0(除了isa),所以不要在init方法里做无谓的重初始化为0的操作。
16:明确 The designated initializer
注释并说明指定的初始化方法。
明确The designated initializer对想要子类化你的类的时候时很重要的。那样,子类化时只需要做一个或多个初始化去保证初值即可。这也有助于在以后调试你的类时明了初始化流程。
17:重写The designated initializer
当重写一个子类并需要init...方法,注意要重写父类的指定初始化方法。
如果你没有正确重写父类的指定初始化方法(注意重写指定初始化方法时,如若需要调用父类的指定初始化方法,否则可能会引起重复调用),你的初始化方法可能不会被调用,这会导致很多微妙而难以排除的错误。
18:成员变量应该定义为@private
参考代码:
@interface MyClass : NSObject {
@private
id myInstanceVariable_;
}
@end
19:以声明时的顺序dealloc处理实例变量
dealloc应该用在@interface声明时同样的顺序处理实例变量,这也有助于评审者鉴别。
代码评审者检查或修正dealloc的实现要确保所有retain的实例变量都获得了释放。
为了简化评审dealloc,将释放retain的实例变量代码保持和@interface里声明的顺序一致。如果dealloc调用了其他方法去释放实例变量,添加注释说明那些实例变量被这些方法所处理了。、、、
20:声明注释
每个接口,类别,协议都必须伴随描述它的用途以及如何整合的注释。
// A delegate for NSApplication to handle notifications about app launch and shutdown. Owned by the main app controller.
@interface MyAppDelegate : NSObject {
...
}
@end
21:注释
注释是使代码保持可读性的极端重要的方式。下面的条款描述了你应该注释什么以及在哪里做注释。但是记住:即使注释是如此重要,最好的代码还是自说明式的。起一个有意义的名字比起一个晦涩的名字然后在用注释去解释它好的多。写注释的时候,记住注释是写给读者,即下一个要理解你的代码并继续开发的人。"下一个"完全可能就是你自己。同样,所有C++编程规范的条款仍然适用。
22:BOOL类型陷阱
整形的转换为BOOL型的时候要小心。不要直接和YES做比较。BOOL在Objective-C里被定义为unsignedchar,这意味着它不仅仅只有YES(1)和NO(0)两个值。不要直接把整形强制转换为BOOL型。常见的错误发生在把数组大小,指针的值或者逻辑位运算的结果赋值到BOOL型中,而这样就导致BOOL值的仅取决于之前整形值的最后一个字节,有可能出现整形值不为0但被转为NO的情况。不要把BOOL型变量直接与YES比较(BOOL其实是unsignedchar类型,不一定等于1)。这样不仅对于精通C的人很有难度,而且此条款的第一点也说明了这样做未必能得到你想要的结果。
测试发现,上述问题已经不存在
BOOL a=-1;
for (int i=-128;i<128; i++) {
a=i;
printf("%i-%i\n",i,a);
}
-128-1
-127-1
-126-1
~~~~
-3-1
-2-1
-1-1
0-0
1-1
2-1
3-1
~~~
126-1
127-1