目录
// GTMFoo.h // FooProject // // Created by Greg Miller on 6/13/08. // Copyright 2008 Google, Inc. All rights reserved. // #import // A sample class demonstrating good Objective-C style. All interfaces, // categories, and protocols (read: all top-level declarations in a header) // MUST be commented. Comments must also be adjacent to the object they're // documenting. // // (no blank line between this comment and the interface) @interface GTMFoo : NSObject { @private NSString *foo_; NSString *bar_; } // Returns an autoreleased instance of GMFoo. See -initWithString: for details // about the argument. + (id)fooWithString:(NSString *)string; // Designated initializer. |string| will be copied and assigned to |foo_|. - (id)initWithString:(NSString *)string; // Gets and sets the string for |foo_|. - (NSString *)foo; - (void)setFoo:(NSString *)newFoo; // Does some work on |blah| and returns YES if the work was completed // successfuly, and NO otherwise. - (BOOL)doWorkWithString:(NSString *)blah; @end
// // GTMFoo.m // FooProject // // Created by Greg Miller on 6/13/08. // Copyright 2008 Google, Inc. All rights reserved. // #import "GTMFoo.h" @implementation GTMFoo + (id)fooWithString:(NSString *)string { return [[[self alloc] initWithString:string] autorelease]; } // Must always override super's designated initializer. - (id)init { return [self initWithString:nil]; } - (id)initWithString:(NSString *)string { if ((self = [super init])) { foo_ = [string copy]; bar_ = [[NSString alloc] initWithFormat:@"hi %d", 3]; } return self; } - (void)dealloc { [foo_ release]; [bar_ release]; [super dealloc]; } - (NSString *)foo { return foo_; } - (void)setFoo:(NSString *)newFoo { [foo_ autorelease]; foo_ = [newFoo copy]; } - (BOOL)doWorkWithString:(NSString *)blah { // ... return NO; } @end
- 使用空格进行缩进,不要在代码中使用制表符,只使用空格,每次缩进两个空格。
- 保持每行宽度为80列。
- (void)doSomethingWithString:(NSString *)theString {
-
星号前的空格是可选的。当写新的代码时,要与原的代码一致。 如果一行有非常多的参数,更好的方式是将每个参数单独拆成一行。如果使用多行,将每个参数前的冒号对齐。
- 星号前的空格是可选的。当写新的代码时,要与原的代码一致。 如果一行有非常多的参数,更好的方式是将每个参数单独拆成一行。如果使用多行,将每个参数前的冒号对齐。
- -或者+与返回类型之间,需要有空格。参数列表中,只有参数之间有空格。
- 方法调用时,所有参数应该在同一行。或者每行一个参数,以冒号对齐。
[myObject doFooWith:arg1 name:arg2 error:arg3];
[myObject doFooWith:arg1 name:arg2 error:arg3];
-
方法定义与方法声明一样,当关键字的长度不足以以冒号对齐时,下一行都要以四个空格进行缩进。
[myObj short:arg1 longKeyword:arg2 evenLongerKeyword:arg3];
-
@public以及@private访问标识符应该以一个空格缩进。.
- 如果你必须使用Objective-C的异常,按下面的格式进行编码代码。然后,请参见避免抛出异常来了解不应该使用异常的原因。
@try { foo(); } @catch (NSException *ex) { bar(ex); } @finally { baz(); }
- 每个@标签应该有独立的一行,在@与{}之间需要有一个空格。
- 尖括号所包括的协议名称与前面的类型标识之间不应该有空格。
- 类名、分类名、协议名用pascal全名法
- 局部变量名,方法名,参数名用camel命名法
- 字段camel命名法,前加_
- 缩写词语全用大写字母
- 常量名(如宏、枚举、静态局部变量等)应该以小写字母k开头,使用混合大小写的格式来分隔单词,如:kInvalidHandle,kWritePerm。
- 方法名应该读起来就像句子,这表示你应该选择与方法名连在一起读起来通顺的参数名。
- 尽量注释很重要,但最好的代码应该自成文档。
- 成员变量应该声明为私有。
- 当你写子类的时候,如果需要init…方法,记得重写父类的指定的初始化器。
- 不要在init方法中,将成员变量初始化为0或者nil,这是冗余的。
- 不要调用NSObject类的类方法new,也不要在子类重写。相反,你应该使用alloc和init方法来创建并初始化一个对象。
- 除非客户端的代码期望使用某个方法,不要把这个方法放进公有的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 Ojbective-C/Objective-C++头文件,#include C/C++头文件。
- 当包含一个使用标准C、C++头文件时,使用#include。头文件应该提供自己的。
- 包含根框架而不是单独的文件。 包含顶级根框架编译器要作更少的工作。根框架通常被预编译,并且加载得更快。另外记得使用#import而不是#include来包含Objective-C的框架。
- 当创建临时对象时,在同一行使用autolease,而不是在同一个方法的后面语句中使用一个单独的release。
- 给对象赋值时遵守autorelease之后retain的模式。
- (void)setFoo:(GMFoo *)aFoo { [foo_ autorelease]; // Won't dealloc if |foo_| == |aFoo| foo_ = [aFoo retain]; }
- 接受NSString作为参数的setter,应该copy它所接受的字符串。
- (void)setFoo:(NSString *)aFoo { [foo_ autorelease]; foo_ = [aFoo copy]; }
- 注意:当使用Objective-C++写基于栈的对象的代码时,如果抛出Objective-C异常,对象不会被清理。
- nil检查只用于逻辑流的判断。
- Ojbective-C中定义BOOL为无符号字符型,这意味着BOOL类型可以有不同于YES(1)或者NO(0)的值。不要直接把整形转换成BOOL。Objective-C的方法签名中,只使用BOOL。 对BOOL使用逻辑运算符(&&, || 和!)是合法的,返回值也可以安全地转换成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]; }
同样的,不要直接比较BOOL变量与YES/NO。不仅仅这影响可读性,结果可能与你想的不同。 错误的用法:
BOOL great = [foo isGreat]; if (great == YES) // ...be great!
正确的用法
BOOL great = [foo isGreat]; if (great) // ...be great!
总结:将常规整形转换成BOOL时要小心,不要直接将BOOL值与YES进行比较。
- 命名 属性所关联的成员变量的命名必须遵守以下划线作为后缀的规则。属性的名字应该与成员变量去掉下划线后缀的名字一模一样。属性的定义必须在@implementation的类定义的最上方。他们的缩进与包含他们的@interface以及@implementation语句一样。
@interface MyClass : NSObject { @private NSString *name_; } @property(copy, nonatomic) NSString *name; @end @implementation MyClass @synthesize name = name_; - (id)init { ... } @end
- NSString属性应该永远被声明为copy特性。 这从逻辑上遵守了NSString的setter必须使用copy而不是retain。
- 不要synthesize CFType的属性 CFType应该永远使用@dynamic实现指示符。 在错误的做法:
@interface MyClass : NSObject @property(readonly) NSString *name; @end @implementation MyClass . . . - (NSString*)name { return @"foo"; } @end
正确的做法
@interface MyClass : NSObject @property(readonly) NSString *name; @end @implementation MyClass @dynamic name; . . . - (NSString*)name { return @"foo"; } @end
- 原子性 一定要注意属性的开销。所有synthesize的setter和getter都是原子的。这会给每个get或者set带来一定的同步开销。显示将你的属性声明为nonatomic除非你需要原子操作。
- 实现委托模式的类应该,
- 拥有一个名为delegate_的成员变量来引用委托。
- 因此,访问器方法应该名为delegate和setDelegate:。
- delegate_对象不应该被retained。
- 分离模型与视图。分离控制器与视图、模型。回调API使用@protocol。
- 模型与视图分离:不要假设模型或者数据源的表示方法。保持数据源与表示层之间的接口抽象。视图不需要了解模型的逻辑(主要的规则是问问你自己,对于数据源的一个实例,有没有可能有多种不同状态的表示方法)。
- 控制器与模型、视图分离:不要把所有的“领域逻辑”放进跟视图有关的类中。这命名得代码非常难以重用。使用控制器来写这些代码,但保证控制器不需要了解太多表示层的逻辑。
- 使用@protocol来定义回调API,如果不是所有的方法都必须实现,使用@optional(例外:当使用Objective-C 1.0,@optional不可用,因此请使用类别来定义“非正式的协议”)。