參考資料:
• Apple: Coding Guidelines for Cocoa
• Google: Objective-C Style Guide
• Three20: Source code style guildelines
正文:
• 格式化代碼
◦ 指針“*”號的位置
▪ 如:NSString *varName;
◦ 空格 VS tabs
▪ 只允許使用空格,將編輯器設(shè)置為1個TAB = 2個字符縮進
◦ 每行的長度
▪ 每行最多不得超過100個字符
▪ 以15寸Macbook Pro的大小,每行100個字符時能最大化地同時容下編輯器和iPhone模擬器
▪ Google的80字符的標準有點少,這導(dǎo)致過于頻繁的換行(Objectve-C的代碼一般都很長)
▪ 通過 “Xcode => Preferences => TextEditing => 勾選Show Page Guide / 輸入
100 => OK” 來設(shè)置提醒
◦ 方法的聲明和定義
▪ 在 – OR + 和返回值之間留1個空格,方法名和第一個參數(shù)間不留空格。如:
- (void)doSomethingWithString:(NSString *)theString {
…
}
▪ 當參數(shù)過長時,每個參數(shù)占用一行,以冒號對齊。如:
- (void)doSomethingWith:(GTMFoo *)theFoo
rect:(NSRect)theRect
interval:(float)theInterval {
…
}
▪ 如果方法名比參數(shù)名短,每個參數(shù)占用一行,至少縮進4個字符,且為垂直對齊(而非使用冒號
對齊)。如:
- (void)short:(GTMFoo *)theFoo
longKeyword:(NSRect)theRect
evenLongerKeyword:(float)theInterval {
…
}
◦ 方法的調(diào)用
▪ 調(diào)用方法沿用聲明方法的習(xí)慣。例外:如果給定源文件已經(jīng)遵從某種習(xí)慣,繼續(xù)遵從那種習(xí)慣。
▪ 所有參數(shù)應(yīng)在同一行中,或者每個參數(shù)占用一行且使用冒號對齊。如:
[myObject doFooWith:arg1 name:arg2 error:arg3];
或
[myObject doFooWith:arg1
name:arg2
error:arg3];
▪ 和方法的聲明一樣,如果無法使用冒號對齊時,每個參數(shù)一行、縮進4個字符、垂直對其(而非
使用冒號對齊)。如:
[myObj short:arg1
longKeyword:arg2
evenLongerKeyword:arg3];
◦ @public 和 @private
▪ @public 和 @private使用單獨一行,且縮進1個字符
◦ Protocals
▪ 類型標示符、代理名稱、尖括號間不留空格。
▪ 該規(guī)則同樣適用于:類聲明、實例變量和方法聲明。如:
@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)的首字母大寫,寫使用首字母大寫的形式
分割單詞
▪ 在面向特定應(yīng)用的代碼中,類名應(yīng)盡量避免使用前綴,每個類都使用相同的前綴影響可讀性。
▪ 在面向多應(yīng)用的代碼中,推薦使用前綴。如:GTMSendMessage
◦ Category Name
▪ 待完善
◦ 方法名
▪ 方法名的首字母小寫,且使用首字母大寫的形式分割單詞。方法的參數(shù)使用相同的規(guī)則。
▪ 方法名+參數(shù)應(yīng)盡量讀起來像一句話(如:)。在這里查看蘋果對方法命名的規(guī)范。
▪ getter的方法名和變量名應(yīng)相同。不允許使用“get”前綴。如:
- (id) getDelegate; // 禁止
- (id)delegate; // 對頭
▪ 本規(guī)則僅針對Objective-C代碼,C++代碼使用C++的習(xí)慣
◦ 變量名
▪ 變量名應(yīng)使用容易意會的應(yīng)用全稱,且首字母小寫,且使用首字母大寫的形式分割單詞
▪ 成員變量使用“_”作為前綴(如:“NSString *_varName;”。雖然這與蘋果的標準(使
用“_”作為后綴)相沖突,但基于以下原因,仍使用“_”作為前綴。
▪ 使用“_”作為前綴,更容易在有代碼自動補全功能的IDE中區(qū)分“屬性
(self.userInfo)”和“成員變量(_userInfo)”
▪ 常量(#define, enums, const等)使用小寫“k”作為前綴,首字母大寫來分割單詞。如:
kInvalidHandle
• 注釋
◦ 待完善
• Cocoa 和 Objective-C特有的規(guī)則
◦ 成員變量使用 @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”,那是多余的
▪ 內(nèi)存中所有的新創(chuàng)建的對象(isa除外)都是0,所以不需要重復(fù)初始化為“0”或“nil”
◦ 避免顯式的調(diào)用 +new 方法
▪ 禁止直接調(diào)用 NSObject 的類方法 +new,也不要在子類中重載它。使用alloc和init方法
◦ 保持公共API的簡潔性
▪ 待完善
◦ #import VS #include
▪ 使用 #import 引入Ojbective-C和Ojbective-C++頭文件,使用 #include 引入C和C++頭
文件
◦ import根框架(root frameworks),而非各單個文件
▪ 雖然有時我們僅需要框架(如Cocoa 或 Foundation)的某幾個頭文件,但引入根文件編譯
器會運行的更快。因為根框架(root frameworks)一般會預(yù)編譯,所以加載會更快。再次強
調(diào):使用 #import 而非 #include 來引入Objective-C框架。如:
#import <Foundation/NSArray.h> // 禁止
#import <Foundation/NSString.h>
…
#import <Foundation/Foundation.h> // 對頭
◦ 創(chuàng)建對象時盡量使用autorelease
▪ 創(chuàng)建臨時對象時,盡量同時在同一行中 autorelease 掉,而非使用單獨的 release 語句
▪ 雖然這樣會稍微有點慢,但這樣可以阻止因為提前 return 或其他意外情況導(dǎo)致的內(nèi)存泄露。
通盤來看這是值得的。如:
// 避免這樣使用(除非有性能的考慮)
MyController* controller = [[MyController alloc] init];
// … 這里的代碼可能會提前return …
[controller release];
// 這樣更好
MyController* controller = [[[MyController alloc] init] autorelease];
◦ 先autorelease,再retain
▪ 在為對象賦值時,遵從“先autorelease,再retain”
▪ 在將一個新創(chuàng)建的對象賦給變量時,要先將舊對象release掉,否則會內(nèi)存泄露。市面上有很
多方法來handle這種情況,這里選擇“先autorelease,再retain”的方法,這種方法不易引
入error。注意:在循環(huán)中這種方法會“填滿”autorelease pool,稍稍影響效率,但是
Google和我(

)認為這個代價是可以接受的。如:
- (void)setFoo:(GMFoo *)aFoo {
[foo_ autorelease]; // 如果foo_和aFoo是同一個對象(foo_ == aFoo),
dealloc不會被調(diào)用
foo_ = [aFoo retain];
}
◦ dealloc的順序要與變量聲明的順序相同
▪ 這有利于review代碼
▪ 如果dealloc中調(diào)用其他方法來release變量,將被release的變量以注釋的形式標注清楚
◦ NSString的屬性的setter使用“copy”
▪ 禁止使用retain,以防止意外的修改了NSString變量的值。如:
- (void)setFoo:(NSString *)aFoo {
[foo_ autorelease];
foo_ = [aFoo copy];
}
或
@property (nonatomic, copy) NSString *aString;
◦ 避免拋出異常(Throwing Exceptions)
▪ 待完善
◦ 對 nil 的檢查
▪ 僅在有業(yè)務(wù)邏輯需求時檢查 nil,而非為了防止崩潰
▪ 向 nil 發(fā)送消息不會導(dǎo)致系統(tǒng)崩潰,Objective-C運行時負責處理。
◦ BOOL陷阱
▪ 將int值轉(zhuǎn)換為BOOL時應(yīng)特別小心。避免直接和YES比較
▪ Objective-C中,BOOL被定義為unsigned char,這意味著除了 YES (1) 和 NO (0)外它
還可以是其他值。禁止將int直接轉(zhuǎn)換(cast or convert)為BOOL。
▪ 常見的錯誤包括:將數(shù)組的大小、指針值或位運算符的結(jié)果轉(zhuǎn)換(cast or convert)為
BOOL,因為該BOOL值的結(jié)果取決于整型值的最后一位
▪ 將整型值轉(zhuǎn)換為BOOL的方法:使用三元運算符返回YES / NO,或使用位運算符(&&, ||, !)
▪ BOOL、_Bool和bool之間的轉(zhuǎn)換是安全的,但是BOOL和Boolean間的轉(zhuǎn)換不是安全的,所以
將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將二者聯(lián)系起來。如:
// abcd.h
@interface MyClass : NSObject {
@private
NSString *_name;
}
@property (copy, nonatomic) NSString *name;
@end
// abcd.m
@implementation MyClass
@synthesize name = _name;
@end
▪ 位置:屬性的聲明緊隨成員變量塊之后,中間空一行,無縮進。如上例所示
▪ 嚴把權(quán)限:對不需要外部修改的屬性使用readonly
▪ NSString使用copy而非retain
▪ CFType使用@dynamic, 禁止使用@synthesize
▪ 除非必須,使用nonatomic
• Cocoa Pattern
◦ Delegate Pattern(委托)
▪ delegate對象使用assign,禁止使用retain。因為retain會導(dǎo)致循環(huán)索引導(dǎo)致內(nèi)存泄露,
并且此類型的內(nèi)存泄露無法被Instrument發(fā)現(xiàn),極難調(diào)試
▪ 成員變量命名為_delegate,屬性名為delegate
◦ Model/View/Controller
▪ Model和View分離
▪ 不多解釋
▪ Controller獨立于View和Controller
▪ 不要在與view相關(guān)的類中添加過多的業(yè)務(wù)邏輯代碼,這讓代碼的可重用性很差
▪ Controller負責業(yè)務(wù)邏輯代碼,且Controller的代碼與view盡量無關(guān)
▪ 使用 @protocal 定義回調(diào)APIs,如果并非所有方法都是必須的,使用 @optional 標示
• 其他
◦ init方法和dealloc方法是是最常用的方法,所以將他們放在類實現(xiàn)的開始位置
◦ 使用空格將相同的變量、屬性對齊,使用換行分組