參考資料:
• Apple: Coding Guidelines for Cocoa
• Google: Objective-C Style Guide
• Three20: Source code style guildelines
正文:
• 格式化代碼
◦ 指針“*”號的位置
▪ 如:NSString *varName;
◦ 空格 VS tabs
▪ 只允許使用空格,將編輯器設置為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
> {
…
}
• 命名
◦ 類名
▪ 類名(及其category name 和 protocal name)的首字母大寫,寫使用首字母大寫的形式
分割單詞
▪ 在面向特定應用的代碼中,類名應盡量避免使用前綴,每個類都使用相同的前綴影響可讀性。
▪ 在面向多應用的代碼中,推薦使用前綴。如:GTMSendMessage
◦ Category Name
▪ 待完善
◦ 方法名
▪ 方法名的首字母小寫,且使用首字母大寫的形式分割單詞。方法的參數使用相同的規則。
▪ 方法名+參數應盡量讀起來像一句話(如:)。在這里查看蘋果對方法命名的規范。
▪ 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。如:
@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,也不要在子類中重載它。使用alloc和init方法
◦ 保持公共API的簡潔性
▪ 待完善
◦ #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];
◦ 先autorelease,再retain
▪ 在為對象賦值時,遵從“先autorelease,再retain”
▪ 在將一個新創建的對象賦給變量時,要先將舊對象release掉,否則會內存泄露。市面上有很
多方法來handle這種情況,這里選擇“先autorelease,再retain”的方法,這種方法不易引
入error。注意:在循環中這種方法會“填滿”autorelease pool,稍稍影響效率,但是
Google和我(

)認為這個代價是可以接受的。如:
- (void)setFoo:(GMFoo *)aFoo {
[foo_ autorelease]; // 如果foo_和aFoo是同一個對象(foo_ == aFoo),
dealloc不會被調用
foo_ = [aFoo retain];
}
◦ 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、_Bool和bool之間的轉換是安全的,但是BOOL和Boolean間的轉換不是安全的,所以
將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];
}
▪ 禁止直接將BOOL和YES/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和Controller
▪ 不要在與view相關的類中添加過多的業務邏輯代碼,這讓代碼的可重用性很差
▪ Controller負責業務邏輯代碼,且Controller的代碼與view盡量無關
▪ 使用 @protocal 定義回調APIs,如果并非所有方法都是必須的,使用 @optional 標示
• 其他
◦ init方法和dealloc方法是是最常用的方法,所以將他們放在類實現的開始位置
◦ 使用空格將相同的變量、屬性對齊,使用換行分組