Objective-C编码风格指南
参考资料:
•Apple:CodingGuidelinesforCocoa
•Google:Objective-CStyleGuide
•Three20:Sourcecodestyleguildelines
正文:
•格式化代码
◦指针“*”号的位置
▪如:NSString*varName;
◦空格VStabs
▪只允许使用空格,将编辑器设置为1个TAB=2个字符缩进
◦每行的长度
▪每行最多不得超过100个字符
▪以15寸MacbookPro的大小,每行100个字符时能最大化地同时容下编辑器和iPhone模拟器
▪Google的80字符的标准有点少,这导致过于频繁的换行(Objectve-C的代码一般都很长)
▪通过“Xcode=>Preferences=>TextEditing=>勾选ShowPageGuide/输入
100=>OK”来设置提醒◦方法的声明和定义
▪在-OR+和返回值之间留1个空格,方法名和第一个参数间不留空格。如:-(void)doSomethingWithString:(NSString*)theString{
...}
▪当参数过长时,每个参数占用一行,以冒号对齐。如:-(void)doSomethingWith:(GTMFoo*)theFoo
rect:(NSRect)theRect
interval:(float)theInterval{
...}
▪如果方法名比参数名短,每个参数占用一行,至少缩进4个字符,且为垂直对齐(而非使用冒号对齐)。如:
-(void)short:(GTMFoo*)theFoo
longKeyword:(NSRect)theRect
evenLongerKeyword:(float)theInterval{
...
}
◦方法的调用
▪调用方法沿用声明方法的习惯。例外:如果给定源文件已经遵从某种习惯,继续遵从那种习惯。
▪所有参数应在同一行中,或者每个参数占用一行且使用冒号对齐。如:
[myObjectdoFooWith:arg1name:arg2error:arg3];
或
[myObjectdoFooWith:arg1
name:arg2
error:arg3];
▪和方法的声明一样,如果无法使用冒号对齐时,每个参数一行、缩进4个字符、垂直对其(而非使用冒号对齐)。如:
[myObjshort:arg1
longKeyword:arg2
evenLongerKeyword:arg3];
◦@public和@private
▪@public和@private使用单独一行,且缩进1个字符
◦Protocals
▪类型标示符、代理名称、尖括号间不留空格。
▪该规则同样适用于:类声明、实例变量和方法声明。如:
@interfaceMyProtocoledClass:NSObject<NSWindowDelegate>{
@private
id<MyFancyDelegate>_delegate;
}
-(void)setDelegate:(id<MyFancyDelegate>)aDelegate;
@end
▪如果类声明中包含多个protocal,每个protocal占用一行,缩进2个字符。如:@interfaceCustomViewController:ViewController<
AbcDelegate,
DefDelegate>{
...}
•命名
◦类名
▪类名(及其categoryname和protocalname)的首字母大写,写使用首字母大写的形式分割单词
▪在面向特定应用的代码中,类名应尽量避免使用前缀,每个类都使用相同的前缀影响可读性。
▪在面向多应用的代码中,推荐使用前缀。如:GTMSendMessage◦CategoryName
▪待完善◦方法名
▪方法名的首字母小写,且使用首字母大写的形式分割单词。方法的参数使用相同的规则。
▪方法名+参数应尽量读起来像一句话(如:)。在这里查看苹果对方法命名的规范。
▪getter的方法名和变量名应相同。不允许使用“get”前缀。如:
-(id)getDelegate;//禁止
-(id)delegate;//对头
▪本规则仅针对Objective-C代码,C++代码使用C++的习惯
◦变量名
▪变量名应使用容易意会的应用全称,且首字母小写,且使用首字母大写的形式分割单词
▪成员变量使用“_”作为前缀(如:“NSString*_varName;”。虽然这与苹果的标准(使
用“_”作为后缀)相冲突,但基于以下原因,仍使用“_”作为前缀。
▪使用“_”作为前缀,更容易在有代码自动补全功能的IDE中区分“属性
(self.userInfo)”和“成员变量(_userInfo)”
▪常量(#define,enums,const等)使用小写“k”作为前缀,首字母大写来分割单词。如:
kInvalidHandle
•注释
◦待完善
•Cocoa和Objective-C特有的规则
成员变量使用@private。如:
@interfaceMyClass:NSObject{
@private
id_myInstanceVariable;
}
//publicaccessors,settertakesownership
-(id)myInstanceVariable;
-(void)setMyInstanceVariable:(id)theVar;
@end
IndentifyDesignatedInitializer▪待完善
OverrideDesingatedInitializer
▪待完善◦初始化
▪在初始化方法中,不要将变量初始化为“0”或“nil”,那是多余的
▪内存中所有的新创建的对象(isa除外)都是0,所以不需要重复初始化为“0”或“nil”◦避免显式的调用+new方法
▪禁止直接调用NSObject的类方法+new,也不要在子类中重载它。使用alloc和init方法◦保持公共API的简洁性
▪待完善
◦#importVS#include
▪使用#import引入Ojbective-C和Ojbective-C++头文件,使用#include引入C和C++头文件
◦import根框架(rootframeworks),而非各单个文件
▪虽然有时我们仅需要框架(如Cocoa或Foundation)的某几个头文件,但引入根文件编译
器会运行的更快。因为根框架(rootframeworks)一般会预编译,所以加载会更快。再次强调:使用#import而非#include来引入Objective-C框架。如:
#import<Foundation/NSArray.h>//禁止
#import<Foundation/NSString.h>
...
#import<Foundation/Foundation.h>//对头◦创建对象时尽量使用autorelease
▪创建临时对象时,尽量同时在同一行中autorelease掉,而非使用单独的release语句
▪虽然这样会稍微有点慢,但这样可以阻止因为提前return或其他意外情况导致的内存泄露。
通盘来看这是值得的。如:
//避免这样使用(除非有性能的考虑)
MyController*controller=[[MyControlleralloc]init];
//...这里的代码可能会提前return...
[controllerrelease];
//这样更好
MyController*controller=[[[MyControlleralloc]init]autorelease];
◦先autorelease,再retain
▪在为对象赋值时,遵从“先autorelease,再retain”
▪在将一个新创建的对象赋给变量时,要先将旧对象release掉,否则会内存泄露。市面上有很
多方法来handle这种情况,这里选择“先autorelease,再retain”的方法,这种方法不易引入error。注意:在循环中这种方法会“填满”autoreleasepool,稍稍影响效率,但是Google和我()认为这个代价是可以接受的。如:
-(void)setFoo:(GMFoo*)aFoo{
[foo_autorelease];//如果foo_和aFoo是同一个对象(foo_==aFoo),dealloc不会被调用
foo_=[aFooretain];
}
◦dealloc的顺序要与变量声明的顺序相同
▪这有利于review代码
▪如果dealloc中调用其他方法来release变量,将被release的变量以注释的形式标注清楚
◦NSString的属性的setter使用“copy”
▪禁止使用retain,以防止意外的修改了NSString变量的值。如:
-(void)setFoo:(NSString*)aFoo{
[foo_autorelease];
foo_=[aFoocopy];
}
或
@property(nonatomic,copy)NSString*aString;
◦避免抛出异常(ThrowingExceptions)▪待完善
◦对nil的检查
▪仅在有业务逻辑需求时检查nil,而非为了防止崩溃
▪向nil发送消息不会导致系统崩溃,Objective-C运行时负责处理。◦BOOL陷阱
▪将int值转换为BOOL时应特别小心。避免直接和YES比较
▪Objective-C中,BOOL被定义为unsignedchar,这意味着除了YES(1)和NO(0)外它
还可以是其他值。禁止将int直接转换(castorconvert)为BOOL。
▪常见的错误包括:将数组的大小、指针值或位运算符的结果转换(castorconvert)为
BOOL,因为该BOOL值的结果取决于整型值的最后一位
▪将整型值转换为BOOL的方法:使用三元运算符返回YES/NO,或使用位运算符(&&,||,!)
▪BOOL、_Bool和bool之间的转换是安全的,但是BOOL和Boolean间的转换不是安全的,所以
将Boolean看成整型值。
▪在Objective-C中,只允许使用BOOL
▪如:
//禁止
-(BOOL)isBold{
return[selffontTraits]&NSFontBoldTrait;
}
-(BOOL)isValid{
return[selfstringValue];
}
//对头
-(BOOL)isBold{
return([selffontTraits]&NSFontBoldTrait)?YES:NO;
}
-(BOOL)isValid{
return[selfstringValue]!=nil;
}
-(BOOL)isEnabled{
return[selfisValid]&&[selfisBold];
}
▪禁止直接将BOOL和YES/NO比较,如://禁止
BOOLgreat=[fooisGreat];
if(great==YES)
...
//对头
BOOLgreat=[fooisGreat];if(great)
...
◦属性
▪命名:与去掉“_”前缀的成员变量相同,使用@synthesize将二者联系起来。如:
//abcd.h
@interfaceMyClass:NSObject{
@private
NSString*_name;
}
@property(copy,nonatomic)NSString*name;
@end
//abcd.m
@implementationMyClass
@synthesizename=_name;
@end
▪位置:属性的声明紧随成员变量块之后,中间空一行,无缩进。如上例所示▪严把权限:对不需要外部修改的属性使用readonly
▪NSString使用copy而非retain
▪CFType使用@dynamic,禁止使用@synthesize
▪除非必须,使用nonatomic•CocoaPattern
◦DelegatePattern(委托)
▪delegate对象使用assign,禁止使用retain。因为retain会导致循环索引导致内存泄露,
并且此类型的内存泄露无法被Instrument发现,极难调试
▪成员变量命名为_delegate,属性名为delegate
◦Model/View/Controller▪Model和View分离
▪不多解释
▪Controller独立于View和Controller
▪不要在与view相关的类中添加过多的业务逻辑代码,这让代码的可重用性很差
▪Controller负责业务逻辑代码,且Controller的代码与view尽量无关
▪使用@protocal定义回调APIs,如果并非所有方法都是必须的,使用@optional标示
•其他
◦init方法和dealloc方法是是最常用的方法,所以将他们放在类实现的开始位置◦使用空格将相同的变量、属性对齐,使用换行分组