Objective-C Coding Style Guidelines

Objective-C  Coding Style  Guidelines

 

 

参考资料:

 

•    Apple:   Coding Guidelines  for  Cocoa

 

•    Google:   Objective-C Style  Guide

 

 

正文:

 

•   间距和格式

 

◦    指针“*”号的位置

 

▪    如:NSString  *varName;

 

◦    空格 VS  tabs

 

▪    只允许使用空格,缩进2个空格

▪    将编辑器设置为1个TAB  =  2个字符缩进

 

◦    每行的长度

 

▪    每行最多不得超过100个字符

▪    以15寸Macbook  Pro的大小,每行100个字符时能最大化地同时容下编辑器和iPhone模拟器

 

▪    Google的80字符的标准有点少,这导致过于频繁的换行(Objectve-C的代码一般都很长)

 

▪    通过 “Xcode => Preferences => TextEditing => 勾选Show Page Guide / 输入 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 {

 

...

 

}

 

◦    方法的调用

 

▪    调用方法沿用声明方法的习惯。例外:如果给定源文件已经遵从某种习惯,继续遵从那种习惯。

 

▪    所有参数应在同一行中,或者每个参数占用一行且使用冒号对齐。如:

[myObject doFooWith:arg1  name:arg2  error:arg3];

 

[myObject doFooWith:arg1

                          name:arg2

                           error:arg3];

 

▪    和方法的声明一样,如果无法使用冒号对齐时,每个参数一行、缩进4个字符、垂直对齐(而非 使用冒号对齐)。如:

[myObj  short:arg1

 

longKeyword:arg2   

evenLongerKeyword:arg3];

 

◦    @public  @private

 

▪    @public  和@private使用单独一行,且缩进1个字符


◦   Protocals

 

▪    类型标示符、代理名称、尖括号间不留空格。

 

▪    该规则同样适用于:类声明、实例变量和方法声明。如:

@interface MyProtocoledClass  :  NSObject<NSWindowDelegate>  {

              @private

 

   id<MyFancyDelegate> _delegate;

 

}

 

               -(void)setDelegate:(id<MyFancyDelegate>)aDelegate;

@end

 

▪  如果类声明中包含多个protocal,每个protocal占用一行,缩进2个字符。如:

 

@interface CustomViewController :ViewController<

    AbcDelegate,

  DefDelegate > {

 

...

 

}

 

•   命名

•      命名要清晰明确, 胜过于简洁

文件名:可以包含被扩展的类的名字,如:GTMNSString+Utils.h  GTMNSTextView+Autocomplete.h。

文件名应该反映了它实现了什么类。遵守你的项目的惯例。

分类命名

应用程序级的代码,应该尽量避免不必要的前缀。为每个类都添加前缀不会提高任何的可读性。当设计跨不同应用程序的代码时,应该使用前缀,例如:GTMSendMessage。 总结:类名、分类名、协议名应该以大写字母开始,并混合小写字母来分隔单词。(Camel命名法)

变量名:

▪  变量名应使用容易意会的应用全称,且首字母小写,且使用首字母大写的形式分割单词。

▪  成员变量使用“_”作为前缀(如:“NSString  *_varName;”。虽然这与苹果的标准(使用“_”作为后缀)相冲突,但基于以下原因,仍使用“_”作为前缀:

 

使用“_”作为前缀,更容易在有代码自动补全功能的IDE中区分“属性(self.userInfo)”和“成员变量(_userInfo)”

 

▪  常量(#define,enums, const等)使用小写“k”作为前缀,首字母大写来分割单词。如: kInvalidHandle

 

错误的命名:

int w;


int nerr;
intnCompConns;


tix =[[NSMutableArray alloc] init];

obj = [someObjectobject];
p = [network port];

正确的命名:

int numErrors;


intnumCompletedConnections;


tickets =[[NSMutableArray alloc] init];

 userInfo = [someObject object];


port = [networkport];

 

insertObject:atIndex: 好

insert:at: 不明确

removeObjectAtIndex: 好

removeObject: 也可以

remove: 不明确

变量命不能以数字开头,不能添加空格,不能添加除下划线以外的特殊字符.

 

零碎:

Prefixes

NS: Foundation

NS: Application Kit

AB: Address Book

IB:   Interface Builder

宏命名

和 ANSIC一样  预处理宏定义都使用大写

类似NSUserDefaults key值使用宏,一直存在于固定头文件中 存储的数据加以注释

 

 

◦   Category Name

 

▪   分类名应该以2~3个字符作为前缀,并且包含所扩展的类名

 

              例如,我们想创建一个NSString的分类用于parsing,就可以将该分类的文件名命名为    

              GTMNSString+Parsing.h,分类名命名为:GTMStringParsingAdditions。分类的方法应该使用分

              类名的前缀(如:gtm_myCategoryMethodOnAString:)

◦    方法名

 

▪    方法名的首字母小写,且使用首字母大写的形式分割单词。方法的参数使用相同的规则。

 

▪    方法名+参数应尽量读起来像一句话(如:)。在这里查看苹果对方法命名的规

▪    getter的方法名和变量名应相同。不允许使用“get”前缀。如:

 

-  (id)getDelegate;  //  错误

-  (id)delegate;       //  正确

 

 

 

 

•   注释

 

1.文件注释

版权信息及作者

每个文件应该按顺序包括如下内容:

版权信息声明(如:Copyright 2008 Google Inc.) 授权样版。选择一个合适的项目所使用的授权样板(例如,Apache 2.0, BSD, LGPL, GPL)。

如果你对其他人的原始代码作出重大的修改,请把你的名字添加到作者里面。当另外一个代码贡献者对文件有问题时,他需要知道怎么联系你,这十分有用。

总结:以版权信息作为文件头部,开始每一个文件,后接文件内容的描述。

2.声明注释

// Adelegate for NSApplication to handle notifications about app // launch andshutdown. Owned by the main app controller. @interface MyAppDelegate : NSObject{

... }

@end

如果你已经在文件头部详细描述了接口,可以直接说明“完整的描述请参见文件头部”,但是一定要 有这部分注释。

另外,公共接口的每个方法,都应该有注释来解释它的作用、参数、返回值以及其它影响。

为类的线程安全性作注释,如果有的话。如果类的实例可以被多个线程访问,记得注释多线程条件

下的使用规则。

总结:每个接口、类别以及协议应该注释,以描述它的目的及作用。

3.实现注释

这会避免二义性,尤其是当符号是一个常用词汇,这使用语句读起来很糟糕。例如,对于符 号"count":

// Sometimeswe need |count| to be less than zero.

或者当引用已经包含引号的符号:

// Rememberto call |StringWithoutSpaces("foo bar baz")|

总结:使用|来引用注释中的变量名及符号名而不是使用引号。

•   Cocoa  Objective-C特有的规则

 

◦    成员变量使用@private。如

 

@interface MyClass  :  NSObject {

 

 @private

 

    id  _myInstanceVariable;

 

}

 

//  public  accessors,  setter takes  ownership

 

         -  (id)myInstanceVariable;

 

-  (void)setMyInstanceVariable:(id)theVar;

         @end

 

◦   Indentify Designated  Initializer

 

◦   Override Desingated  Initializer


 

◦    初始化

 

▪    在初始化方法中,不要将变量初始化为“0”或“nil”,那是多余的

内存中所有的新创建的对象(isa除外)都是0,所以不需要重复初始化为“0”或“nil”

 

◦    避免使用new方法

 

▪    禁止直接调用 NSObject 的类方法 +new,也不要在子类中重载它。使用allocinit方法

 

◦    保持公共API的简洁性

 

▪    保持类的简洁,如果一个方法是不需要公开的,那就别公开。可以使用一个私有的分类来使公开的API简洁例如:

 // GTMFoo.m 
#import "GTMFoo.h"  
 
@interface GTMFoo (PrivateDelegateHandling) 
- (NSString *)doSomethingWithDelegate;  // Declare private method 
@end  
 
@implementation GTMFoo(PrivateDelegateHandling) 
... 
- (NSString *)doSomethingWithDelegate {   
// Implement this method 
} 
... 
@end

 

◦   #import VS  #include

 

▪    使用 #import 引入Ojbective-C和Ojbective-C++头文件,使用 #include引入C和C++头

文件

◦    import根框架root  frameworks),而非各单个文件

 

▪    虽然有时我们仅需要框架(如Cocoa  或 Foundation)的某几个头文件,但引入根文件编译

 

器会运行的更快。因为根框架(root frameworks)一般会预编译,所以加载会更快。再次强 调:使用 #import 而非 #include 来引入Objective-C框架。如:

#import <Foundation/NSArray.h>        //错误

#import <Foundation/NSString.h>

 

...

 

#import  <Foundation/Foundation.h>  //正确

◦    创建临时对象时尽量使用autorelease

 

▪    创建临时对象时,尽量同时在同一行中 autorelease  掉,而非使用单独的 release  语句 。

 

虽然这样会稍微有点慢,但这样可以阻止因为提前 return 或其他意外情况导致的内存泄露。通盘来看这是值得的。如:

    // 避免这样使用除非有性能的考虑

    MyController*  controller =  [[MyController  alloc] init];

 

//     ...  这里的代码可能会提前return  ...

 

[controller  release];

 

// 这样更好

 

MyController*  controller =  [[[MyController  alloc] init]  autorelease];

 

◦    autoreleaseretain

 

▪    在为对象赋值时,遵从“先autorelease,再retain” 。

 

在将一个新创建的对象赋给变量时,要先将旧对象release掉,否则会内存泄露。市面上有很多方法来handle这种情况,这里选择“先autorelease,再retain”的方法,这种方法不易引入error。

         注意:在循环中这种方法会“填满”autorelease pool,稍稍影响效率,但是 Google认为 

                    这个代价是可以接受的。如:

- (void)setFoo:(GMFoo  *)aFoo  {

 

  [_foo autorelease];  // 如果_fooaFoo是同一个对象_foo == aFoo),dealloc不会被调用

  _foo =  [aFoo retain];

 

}

 

 

◦    self格式的strong型变量

retain型全局变量,使用self形式定义,以nil形式释放,简单、安全。

◦    dealloc的顺序要与变量声明的顺序相同

 

▪    这有利于review代码

 

如果dealloc中调用其他方法来release变量,将被release的变量以注释的形式标注清楚

 

◦    NSString的属性的setter使用copy

 

▪    禁止使用retain,以防止意外的修改了NSString变量的值。如:

 

- (void)setFoo:(NSString *)aFoo { [foo_autorelease];

 

foo_  =  [aFoo copy];

 

}

@property (nonatomic,  copy)  NSString *aString;

 

◦    避免抛出异常Throwing Exceptions

 

▪    待完善

◦    nil 的检查


▪       仅在有业务逻辑需求时检查 nil而非为了防止崩溃

               nil发送消息不会导致系统崩溃Objective-C运行时负责处理。

 

◦    BOOL陷阱

 

▪    将int值转换为BOOL时应特别小心。避免直接和YES比较

▪    Objective-C中,BOOL被定义为unsigned char,这意味着除了YES (1) 和 NO (0)外它还可以是其他值。禁止将int直接转换(cast or convert)为BOOL

 

▪    常见的错误包括:将数组的大小、指针值或位运算符的结果转换(cast or convert)为 BOOL,因为该BOOL值的结果取决于整型值的最后一位

 

▪  将整型值转换为BOOL的方法:使用三元运算符返回YES/   NO或使用位运算符(&&,||,!

 

▪    BOOL_Boolbool之间的转换是安全的,但是BOOLBoolean间的转换不是安全的,所以将Boolean看成整型值。

 

▪    在Objective-C中,只允许使用BOOL

 

▪    如:

 

//  错误

 

-  (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];

 

}

 

禁止直接将BOOLYES/NO比较,如:// 错误

 

BOOL great = [foo isGreat];

if (great == YES)

 

...

 

//  正确

 

BOOL great = [foo isGreat];

if (great)

...

 

◦    属性

 

▪    命名与去掉“_”前缀的成员变量相同使用@synthesize将二者联系起来。如

 

//  abcd.h

 

@interface MyClass : NSObject{

  @private

 

    NSString *_name;

 

}

@property (copy,  nonatomic)  NSString *name;

 

@end

 

// abcd.m

@implementation MyClass @synthesize name = _name; @end


▪    位置:属性的声明紧随成员变量块之后,无缩进。如上例所示

 

▪    严把权限:对不需要外部修改的属性使用readonly

 

▪    NSString使用copy而非retain

 

▪    CFType使用@dynamic禁止使用@synthesize

 

▪    除非必须使用nonatomic

 

•   Cocoa  Pattern

 

◦    Delegate  Pattern委托

 

▪    delegate对象使用assign,禁止使用retain。因为retain会导致循环索引导致内存泄露,并且此类型的内存泄露无法被Instrument发现,极难调试

▪    成员变量命名为_delegate,属性名为delegate

 

◦   Model/View/Controller

 

▪    Model和View分离

 

▪    Controller独立于View和Model

 

▪    不要在与view相关的类中添加过多的业务逻辑代码,这让代码的可重用性很差

▪    Controller负责业务逻辑代码,且Controller的代码与view尽量无关

 

▪    使用 @protocal  定义回调APIs,如果并非所有方法都是必须的,使用 @optional标示

 

•   其他

 

◦    init方法和dealloc方法是是最常用的方法所以将他们放在类实现的开始位置

 

◦    使用空格将相同的变量、属性对齐使用换行分组

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值