簡介:
本文整理自Apple文檔《Coding Guidelines for Cocoa》。這份文檔原意是給Cocoa框架、插件及公共API開發者提供一些編碼指導,實質上相當於Apple內部的編碼規范。在多人協作時,一份統一的代碼規范大大減少開發者之間的溝通成本,極力推薦。
目錄:
一、代碼命名基礎
二、方法
三、函數
四、Property及其他
五、縮寫
一、代碼命名基礎
1.通用原則
1.1 清晰
盡量清晰又簡潔,無法兩全時清晰更重要
通常不應縮寫名稱,即使方法名很長也應完整拼寫
你可能認為某個縮寫眾所周知,但其實未必,特別是你周圍的開發者語言文化背景不同時
有一些歷史悠久的縮寫還是可以使用的,詳見第五章
API命名避免歧義,例如一個方法名有多種理解
1.2 一致
盡力保持Cocoa編程接口命名一致
如果有疑惑,請浏覽當前頭文件或者參考文檔
當某個類的方法使用了多態時,一致性尤其重要
不同類裡,功能相同的方法命名也應相同
1.3 避免自引用(self Reference)
命名不應自引用
這裡的自引用指的是在詞尾引用自身
Mask與Notification忽略此規則
2.前綴
前綴是編程接口命名的重要部分,它們區分了軟件的不同功能區域:
前綴可以防止第三方開發者與Apple的命名沖突
同樣可以防止Apple內部的命名沖突
前綴有指定格式
它由二到三個大寫字母組成,不使用下劃線和子前綴
命名類、協議、函數、常量和typedef結構體時使用前綴
方法名不使用前綴(因為它存在於特定類的命名空間中)
結構體字段不使用前綴
3.書寫約定
在命名API元素時, 使用駝峰命名法(如runTheWordsTogether),並注意以下書寫約定:
方法名
小寫第一個字母,大寫之後所有單詞的首字母,不使用前綴
如果方法名以一個眾所周知的大寫縮略詞開始,該規則不適用
如TIFFRepresentation (NSImage)
fileExistsAtPath:isDirectory:
函數及常量名
使用與其關聯類相同的前綴,並大寫首字母
NSRunAlertPanel NSCellDisabled
標點符號
由多個單詞組成的名稱,別使用標點符號作為名稱的一部分
分隔符(下劃線、破折號等)也不能使用
避免使用下劃線作為私有方法的前綴,Apple保留這一方式的使用
強行使用可能會導致命名沖突,即Apple已有的方法被覆蓋,這會導致災難性後果
實例變量使用下劃線作為前綴還是允許的
4.class與protocol命名
class
class的名稱應該包含一個名詞,用以表明這個類是什麼(或者做了什麼),並擁有合適的前綴
如NSString、NSDate、NSScanner、UIApplication、UIButton
不關聯class的protocol
大多數protocol聚集了一堆相關方法,並不關聯class
這種protocol使用ing形式以和class區分開來
關聯class的protocol
一些protoco聚集了一堆無關方法,並試圖與某個class關聯在一起,由這個class來主導
這種protocol與class同名
如NSObject protocol
5.頭文件
頭文件的命名極其重要,因為它可以指出頭文件包含的內容:
聲明一個孤立的class或protocol
將聲明放入單獨的文件
使頭文件名與聲明的class/protocol相同
聲明關聯的class或protocol
將關聯的聲明(class/category/protocol)放入同一個頭文件
頭文件名與主要的class/category/protocol相同
Framework頭文件
每個framework都應該有一個同名頭文件
Include了框架內其他所有頭文件
添加API到另一個framework
如果要在一個framework中為另一個framework的class catetgory添加方法,加上單詞“Additions”
如Application Kit的NSBundleAdditions.h 文件
關聯的函數、數據類型
如果你有一組關聯的函數、常量、結構體或其他數據類型,將它們放到一個名字合適的頭文件中
如Application Kit的NSGraphics.h
二、方法
1.通用原則
以小寫字母開始,之後單詞的首字母大寫
以眾所周知的縮寫開始可以大寫,如TIFF、PDF
私有方法可以加前綴
如果方法代表對象接收的動作,以動詞開始
不要使用 do 或 does 作為名字的一部分,因為助動詞在這裡很少有實際意義
同樣的,也別在動詞之前使用副詞和形容詞
- (void)invokeWithTarget:(id)target; - (void)selectTabViewItem:(NSTabViewItem *)tabViewItem
如果方法返回接收者的屬性,以 接收者 + 接收的屬性 命名
除非間接返回多個值,否則不要使用 get 單詞(為了與accessor methods區分)
在所有參數之前使用關鍵字
確保參數之前的關鍵字充分描述了參數
創建自定義 init 方法時,記得指明關聯的元素
不要使用 and 來連接作為接收者屬性的關鍵字
雖然下面的例子使用 and 看似不錯,但是一旦參數非常多時就容易出現問題
除非方法描述了兩個獨立的操作,才使用 and 來連接它們
2.getter和setter方法(Accessor Methods)
如果property表示為名詞,格式如下
- (type)noun;
- (void)setNoun:(type)aNoun;
- (BOOL)isAdjective;
- (NSString *)title; - (void)setTitle:(NSString *)aTitle;
如果property表示為形容詞,格式如下
- (BOOL)isAdjective;
- (void)setAdjective:(BOOL)flag;
- (BOOL)isEditable; - (void)setEditable:(BOOL)flag;
如果property表示為動詞,格式如下(動詞用一般現在時)
- (BOOL)verbObject;
- (void)setVerbObject:(BOOL)flag;
- (BOOL)showsAlpha; - (void)setShowsAlpha:(BOOL)flag;
不要把動詞的過去分詞形式當作形容詞使用
你可能使用情態動詞(can、should、will等)來增加可讀性,不過不要使用 do或 does
只有方法需要間接返回多個值的情況下才使用 get
像這種接收多個參數的方法應該能夠傳入nil,因為調用者未必對每個參數都感興趣
3.Delegate方法
以發送消息的對象開始
省略了前綴的類名和首字母小寫
- (BOOL)tableView:(NSTableView *)tableView shouldSelectRow:(int)row; - (BOOL)application:(NSApplication *)sender openFile:(NSString *)filename;
以發送消息的對象開始的規則不適用下列兩種情況
只有一個sender參數的方法
- (BOOL)applicationOpenUntitledFile:(NSApplication *)sender;
響應notification的方法(方法的唯一參數就是notification)
- (void)windowDidChangeScreen:(NSNotification *)notification;
使用單詞 did 和 will 來通知delegate
did 表示某些事已發生
will 表示某些事將要發生
- (void)browserDidScroll:(NSBrowser *)sender; - (NSUndoManager *)windowWillReturnUndoManager:(NSWindow *)window;
詢問delegate是否可以執行某個行為時可以使用 did 或 will,不過 should 更完美
- (BOOL)windowShouldClose:(id)sender;
4.集合方法
為了管理集合中的元素,集合應該有這幾個方法
- (void)addElement:(elementType)anObj;
- (void)removeElement:(elementType)anObj;
- (NSArray *)elements;
- (void)addLayoutManager:(NSLayoutManager *)obj; - (void)removeLayoutManager:(NSLayoutManager *)obj; - (NSArray *)layoutManagers;
如果集合是無序的,返回一個NSSet比NSarray更好
如果需要在集合中的特定位置插入元素,使用類似下面的方法
- (void)insertLayoutManager:(NSLayoutManager *)obj atIndex:(int)index; - (void)removeLayoutManagerAtIndex:(int)index;
其他集合方法示例
- (void)addChildWindow:(NSWindow *)childWin ordered:(NSWindowOrderingMode)place; - (void)removeChildWindow:(NSWindow *)childWin; - (NSArray *)childWindows; - (NSWindow *)parentWindow; - (void)setParentWindow:(NSWindow *)window;
5.方法參數
參數名以小寫字母開始,之後的單詞首字母大寫
如:removeObject:(id)anObject
別使用 ”pointer” 或 ”ptr” 命名
參數類型裡就已表明它是否是一個指針
避免只有一到二個字母的參數名
避免只有幾個字母的縮寫
...action:(SEL)aSelector ...alignment:(int)mode ...atIndex:(int)index ...content:(NSRect)aRect ...doubleValue:(double)aDouble ...floatValue:(float)aFloat ...font:(NSFont *)fontObj ...frame:(NSRect)frameRect ...intValue:(int)anInt ...keyEquivalent:(NSString *)charCode ...length:(int)numBytes ...point:(NSPoint)aPoint ...stringValue:(NSString *)aString ...tag:(int)anInt ...target:(id)anObject ...title:(NSString *)aString
6.私有方法
不要使用下劃線作為私有方法的前綴,Apple保留這一使用方式
因為若是你的私有方法名已被Apple使用,覆蓋它將會產生極難追蹤的BUG
如果繼承自大型Cocoa框架(如UIView),請確保子類的私有方法名與父類不一樣
可以為私有方法加一個前綴,如公司名或項目名:XX_
例如你的項目叫做Byte Flogger,那麼前綴可能是:BF_addObject
總之,為子類的私有方法添加前綴是為了不覆蓋其父類的私有方法
三、函數
函數的命名類似方法,但有兩點要注意
你使用的類和常量擁有相同的前綴
前綴後的首字母大寫
許多函數名以描述其作用的動詞開始
NSHighlightRect NSDeallocateObject
查詢屬性的函數有進一步的命名規則
如果函數返回首個參數的屬性,省略動詞
unsigned int NSEventMaskFromType(NSEventType type) float NSHeight(NSRect aRect)
如果通過reference返回了值,使用 “Get”
const char *NSGetSizeAndAlignment(const char *typePtr, unsigned int *sizep, unsigned int *alignp)
如果返回的是boolean值,應該靈活使用動詞
BOOL NSDecimalIsNotANumber(const NSDecimal *decimal)
四、Property及其他
1.Property與實例變量
1.1 Property
Property命名規則與第二章accessor methods一樣(因為兩者緊密聯系)
如果property表示為一個名詞或動詞,格式如下
@property (…) 類型 名詞/動詞 ;
@property (strong) NSString *title; @property (assign) BOOL showsAlpha;
如果property表示為一個形容詞
可省略 ”is” 前綴
但要指定getter方法的慣用名稱
@property (assign, getter=isEditable) BOOL editable;
1.2 實例變量
通常不應該直接訪問實例變量
init、dealloc、accessor methods等方法內部例外
實例變量以下劃線 “_” 開始
確保實例變量描述了所存儲的屬性
@implementation MyClass { BOOL _showsTitle; }
如果想要修改property的實例變量名,使用 @synthesize語句
@implementation MyClass @synthesize showsTitle=_showsTitle;
為一個class添加實例變量時,有幾點需要注意:
避免聲明公有實例變量
開發者關注的應該是對象接口,而不是其數據細節
你可以通過使用property來避免聲明實例變量
如果需要聲明實例變量,指定關鍵字@private 或 @protected
如果你希望子類可以直接訪問某個實例變量,使用 @protected 關鍵字
如果一個實例變量是某個類可訪問的屬性,確保寫了accessor methods
如果有可能,還是使用property
2.常量
2.1 枚舉常量
使用枚舉來關聯一組integer常量
枚舉常量和typedef遵循函數的命名規范,下面的例子是 NSMatrix.h
typedef enum _NSMatrixMode { NSRadioModeMatrix = 0, NSHighlightModeMatrix = 1, NSListModeMatrix = 2, NSTrackModeMatrix = 3 } NSMatrixMode;
你可以為bit masks之類的東西創建一個匿名枚舉
enum { NSBorderlessWindowMask = 0, NSTitledWindowMask = 1 << 0, NSClosableWindowMask = 1 << 1, NSMiniaturizableWindowMask = 1 << 2, NSResizableWindowMask = 1 << 3 };
2.2 使用const關鍵字的常量
使用const關鍵字來創建一個float常量
你可以使用const關鍵字來創建一個與其他常量不相關的integer常量,否則,使用枚舉
使用const關鍵字的常量也遵循函數的命名規則
const float NSLightGray;
2.3 其他常量類型
通常不應使用 #define 預編譯指令來創建常量
integer常量,使用枚舉
float常量,使用 const 修飾符
對 #define 預編譯指令,大寫所有字母
比如 DEBUG 判斷
#ifdef DEBUG
注意宏命令的字首和字尾都有雙下劃線
__MACH__
定義NSString常量來作為Notification和Key值
這樣做可以有效防止拼寫錯誤
APPKIT_EXTERN NSString *NSPrintCopies;
3.Notifications與Exceptions
3.1 Notifications
Notification的格式
[Name of associated class] + [Did | Will] + [UniquePartOfName] + Notification
NSApplicationDidBecomeActiveNotification NSWindowDidMiniaturizeNotification NSTextViewDidChangeSelectionNotification NSColorPanelColorDidChangeNotification
3.2 Exceptions
Exception的格式
[Prefix] + [UniquePartOfName] + Exception
NSColorListIOException NSColorListNotEditableException NSDraggingException NSFontUnavailableException NSIllegalSelectorException
五、縮寫
設計編程接口時通常不應使用縮寫,但下列已被廣泛使用的縮寫名稱除外
標准C庫中的縮寫名,如:alloc、init
參數名可自由使用縮寫,如:imageRep、col、obj、otherWin