Version: 0.01
本文部份章節(jié)摘自《蘋(píng)果 Cocoa 編碼規(guī)范》(即Apple’s Cocoa Coding Guidelines)、《Google的Objective-C編碼規(guī)范》(即Google Objective-C Style Guide)。
- 命名
* 一般性原則
* 清晰性
* 一致性
* 不要自我指涉
* 前綴
* 書(shū)寫(xiě)約定
* 類(lèi)與協(xié)議命名
* 頭文件 - 方法命名
* 一般性規(guī)則
* 訪問(wèn)方法
* 委托方法
* 集合方法
* 方法參數(shù)
* 私有方法
* 函數(shù)命名 - 實(shí)例變量與數(shù)據(jù)類(lèi)型命名
* 實(shí)例變量
* 枚舉常量
* const 常量
* 字符串常量
* 其他常量 - 異常與通知
* 異常
* 通知 - 可接受的縮略名
* 常見(jiàn)的縮寫(xiě)
* 常見(jiàn)的略寫(xiě) - 編碼
* 花括號(hào)的使用
* 空格的使用
* 空行的使用
* 縮進(jìn)的使用
* 折行的使用 - 注釋
* 文檔注釋
* 代碼注釋 - 約定俗成的習(xí)慣
* 成員變量定義
* 屬性定義
* 比較操作
命名
在面向?qū)ο筌浖?kù)的設(shè)計(jì)過(guò)程中,開(kāi)發(fā)人員經(jīng)常忽視對(duì)類(lèi)、方法、函數(shù)、常量以及其他編程接口元素的命名。本節(jié)討論大多數(shù) Cocoa 接口的一些命名約定。
一般性原則
清晰性
- 最好是既清晰又簡(jiǎn)短,但不要為簡(jiǎn)短而喪失清晰性;
| 代碼 | 點(diǎn)評(píng) |
|---|---|
| insertObject:atIndex: | good |
| insert:at: | 不清晰;要插入什么?“at”表示什么? |
| removeObjectAtIndex: | good |
| removeObject: | 這樣也不錯(cuò),可以針對(duì)特定的對(duì)象進(jìn)行移除。 |
| remove | 不清晰;要移除什么? |
- 名稱(chēng)通常不使用縮寫(xiě),即使名稱(chēng)很長(zhǎng),也要拼寫(xiě)完全;
| 代碼 | 點(diǎn)評(píng) |
|---|---|
| destinationSelection | good |
| destSel | 不清晰 |
| setBackgroundColor: | good |
| setBkgdColor: | 不清晰 |
你可能會(huì)認(rèn)為某個(gè)縮寫(xiě)廣為人知,但有可能并非如此,尤其是當(dāng)你的代碼被來(lái)自不同文化和語(yǔ)言背景的開(kāi)
發(fā)人員所使用時(shí)。
然而,你可以使用少數(shù)非常常見(jiàn),歷史悠久的縮寫(xiě)。請(qǐng)參考:“
可接受的縮略名”一節(jié);避免使用有歧義的 API 名稱(chēng),如那些能被理解成多種意思的方法名稱(chēng)。
| 代碼 | 點(diǎn)評(píng) |
|---|---|
| sendPort | 是發(fā)送端口還是返回一個(gè)發(fā)送端口? |
| displayName | 是顯示名稱(chēng)還是返回用于展示的名稱(chēng)? |
一致性
盡可能使用與 Cocoa 編程接口命名保持一致的名稱(chēng)。如果你不太確定某個(gè)命名是否一致,可以瀏覽一下頭文件或參考文檔中的范例;
在使用多態(tài)方法的類(lèi)中,命名的一致性非常重要。在不同類(lèi)中實(shí)現(xiàn)相同功能的方法應(yīng)該具有相同的名稱(chēng)。
| 代碼 | 點(diǎn)評(píng) |
|---|---|
- (int)tag |
在 NSView, NSCell, NSControl 中有定義 |
- (void)setStringValue:(NSString *) |
在 Cocoa 的許多類(lèi)中有定義 |
請(qǐng)參考“方法參數(shù)”一節(jié)。
不要自我指涉
- 不要自我指涉;
| 代碼 | 點(diǎn)評(píng) |
|---|---|
| NSString | okey |
| NSStringObject | 自我指涉 |
- 掩碼(可使用位操作進(jìn)行組合)和用作通知名稱(chēng)的常量不受該約定限制。
| 代碼 | 點(diǎn)評(píng) |
|---|---|
| NSUnderlineByWordMask | okey |
| NSTableViewColumnDidMoveNotification | okey |
前綴
前綴是名稱(chēng)的重要組成部分。它們可以區(qū)分軟件的功能范疇。通常,軟件會(huì)被打包成一個(gè)框架或多個(gè)緊密相關(guān)的框架(如 Foundation 和 Application Kit 框架)。前綴可以防止第三方開(kāi)發(fā)者與蘋(píng)果公司之間的命名沖
突(同樣也可防止蘋(píng)果內(nèi)部不同框架之間的命名沖突)。
- 前綴有規(guī)定的格式。它由兩到三個(gè)大寫(xiě)字符組成,不能使用下劃線或子前綴;
| 前綴 | Cocoa 框架 |
|---|---|
| NS | Foundation |
| NS | Application Kit |
| AB | Address Book |
| IB | Interface Builder |
命名 class, protocol, structure, function, constant, enumeration 時(shí)使用前綴;
命名 method 時(shí)不使用前綴,因?yàn)榉椒ㄒ呀?jīng)在它所在類(lèi)的命名空間中;同理,命名結(jié)構(gòu)體成員時(shí)也不使用前綴。
書(shū)寫(xiě)約定
在為 API 元素命名時(shí),請(qǐng)遵循如下一些簡(jiǎn)單的書(shū)寫(xiě)約定:
對(duì)于包含多個(gè)單詞的名稱(chēng),不要使用標(biāo)點(diǎn)符號(hào)作為名稱(chēng)的一部分或作為分隔符(下劃線,破折號(hào)等);
此外,大寫(xiě)每個(gè)單詞的首字符并將這些單詞連續(xù)拼寫(xiě)在一起。請(qǐng)注意以下限制:方法名小寫(xiě)第一個(gè)單詞的首字符,大寫(xiě)后續(xù)所有單詞的首字符。方法名不使用前綴,如: fileExistsAtPath:isDirectory: ;
如果方法名以一個(gè)廣為人知的大寫(xiě)首字母縮略詞開(kāi)頭,該規(guī)則不適用,如: NSImage 中的 TIFFRepresentation ;函數(shù)名和常量名使用與其關(guān)聯(lián)類(lèi)相同的前綴,并且要大寫(xiě)前綴后面所有單詞的首字符,如: NSRunAlertPanel, NSCellDisabled ;
避免使用下劃線來(lái)表示名稱(chēng)的私有含義。蘋(píng)果公司保留該方式的使用。如果第三方這樣使用可能會(huì)導(dǎo)致命名沖突,他們可能會(huì)在無(wú)意中用自己的方法覆蓋掉已有的私有方法,這會(huì)導(dǎo)致嚴(yán)重的后果。請(qǐng)參考“
私有方法”一節(jié)以了解私有 API 的命名約定。
類(lèi)與協(xié)議命名
類(lèi)名應(yīng)包含一個(gè)明確描述該類(lèi)(或類(lèi)的對(duì)象)是什么或做什么的名詞。類(lèi)名要有合適的前綴(請(qǐng)參考“前綴”一節(jié))。 Foundation 及 Application Kit 有很多這樣例子,如: NSString, NSData, NSScanner, NSApplication, NSButton 以及 NSEvent 。
協(xié)議應(yīng)該根據(jù)對(duì)方法的行為進(jìn)行分組的方式來(lái)命名。
- 大多數(shù)協(xié)議僅包含一組相關(guān)的方法,而不關(guān)聯(lián)任何類(lèi),這種協(xié)議的命名應(yīng)該使用動(dòng)名詞(ing),以避免不與類(lèi)名混淆;
| 代碼 | 點(diǎn)評(píng) |
|---|---|
| NSLocking | good |
| NSLock | 糟糕,它看起來(lái)像類(lèi)名 |
- 有些協(xié)議包含一些彼此無(wú)關(guān)的方法(這樣做是避免創(chuàng)建多個(gè)獨(dú)立的小協(xié)議)。這樣的協(xié)議傾向于與某個(gè)類(lèi)關(guān)聯(lián)在一 起,該類(lèi)是協(xié)議的主要體現(xiàn)者。在這種情況下,我們約定協(xié)議的名稱(chēng)與該類(lèi)同名。 NSObject 協(xié)議就是這樣一個(gè)例子。這個(gè)協(xié)議組合一組彼此無(wú)關(guān)的方法,有用于查詢對(duì)象在其類(lèi)層次中位置的方法,有使之能調(diào)用特殊方法的方法以及用于增減引用計(jì)數(shù)的方 法。由于 NSObject 是這些方法的主要體現(xiàn)者,所以我們用類(lèi)的名稱(chēng)命名這個(gè)協(xié)議。
頭文件
頭文件的命名方式很重要,我們可以根據(jù)其命名知曉頭文件的內(nèi)容。
- 聲明孤立的類(lèi)或協(xié)議:將孤立的類(lèi)或協(xié)議聲明放置在單獨(dú)的頭文件中,該頭文件名稱(chēng)與類(lèi)或協(xié)議同名;
| 頭文件 | 聲明 |
|---|---|
| NSApplication.h | NSApplication 類(lèi) |
- 聲明相關(guān)聯(lián)的類(lèi)或協(xié)議:將相關(guān)聯(lián)的聲明(類(lèi)、類(lèi)別及協(xié)議)放置在一個(gè)頭文件中,該頭文件名稱(chēng)與主要的類(lèi)/類(lèi)別/協(xié)議的名字相同;
| 頭文件 | 聲明 |
|---|---|
| NSString.h | NSString 和 NSMutableString 類(lèi) |
| NSLock.h | NSLocking 協(xié)議和 NSLock, NSConditionLock, NSRecursiveLock 類(lèi) |
- 包含框架頭文件:每個(gè)框架應(yīng)該包含一個(gè)與框架同名的頭文件,該頭文件包含該框架所有公開(kāi)的頭文件;
| 頭文件 | 框架 |
|---|---|
| Foundation.h | Foundation.framework |
為已有框架中的某個(gè)類(lèi)擴(kuò)展 API :如果要在一個(gè)框架中聲明屬于另一個(gè)框架某個(gè)類(lèi)的范疇類(lèi)的方法, 該頭文件的命名形式為:原類(lèi)名+“Additions”。如 Application Kit 中的 NSBundleAdditions.h ;
相關(guān)聯(lián)的函數(shù)與數(shù)據(jù)類(lèi)型:將相聯(lián)的函數(shù)、常量、結(jié)構(gòu)體以及其他數(shù)據(jù)類(lèi)型放置到一個(gè)頭文件中,并以合適的名字命名。如 Application Kit 中的 NSGraphics.h 。
方法命名
一般性規(guī)則
為方法命名時(shí),請(qǐng)考慮如下一些一般性規(guī)則:
-
小寫(xiě)第一個(gè)單詞的首字符,大寫(xiě)隨后單詞的首字符,不使用前綴。請(qǐng)參考“
書(shū)寫(xiě)約定”一節(jié)。有兩種例外情況:- 方法名以廣為人知的大寫(xiě)字母縮略詞(如: TIFF or PDF )開(kāi)頭;
- 私有方法可以使用統(tǒng)一的前綴來(lái)分組和辨識(shí),請(qǐng)參考“
私有方法”一節(jié)。
-
表示對(duì)象行為的方法,名稱(chēng)以動(dòng)詞開(kāi)頭:
- (void)invokeWithTarget:(id)target: - (void)selectTabViewItem:(NSTableViewItem *)tableViewItem名稱(chēng)中不要出現(xiàn) do 或 does,因?yàn)檫@些助動(dòng)詞沒(méi)什么實(shí)際意義。也不要在動(dòng)詞前使用副詞或形容詞修飾。
如果方法返回對(duì)象的某個(gè)屬性,直接用屬性名稱(chēng)命名。不要使用 get,除非是間接返回一個(gè)或多個(gè)值。請(qǐng)參考“
訪問(wèn)方法”一節(jié);
| 代碼 | 點(diǎn)評(píng) |
|---|---|
- (NSSize)cellSize; |
對(duì) |
- (NSSize)calcCellSize; |
錯(cuò) |
- (NSSize)getCellSize; |
錯(cuò) |
- 參數(shù)要用描述該參數(shù)的關(guān)鍵字命名;
| 代碼 | 點(diǎn)評(píng) |
|---|---|
- (void)sendAction:(SEL)aSelector to:(id)anObject forAllCells:(BOOL)flag; |
對(duì) |
- (void)sendAction:(SEL)aSelector :(id)anObject :(BOOL)flag; |
錯(cuò) |
- 參數(shù)前面的單詞要能描述該參數(shù);
| 代碼 | 點(diǎn)評(píng) |
|---|---|
- (id)viewWithTag:(int)aTag; |
對(duì) |
- (id)taggedView:(int)aTag; |
錯(cuò) |
- 細(xì)化基類(lèi)中的已有方法:創(chuàng)建一個(gè)新方法,其名稱(chēng)是在被細(xì)化方法名稱(chēng)后面追加參數(shù)關(guān)鍵詞;
| 代碼 | 所在類(lèi) |
|---|---|
- (id)initWithFrame:(NSRect)frameRect; |
NSView |
- (id)initWithFrame:(NSRect)frameRect mode:(ind)aMode cellClass:(Class)factoryId numberOfRows:(int)rowsHigh numberOfColumns:(int)colsWide; |
NSMatrix - NSView的子類(lèi) |
- 不要使用 and 來(lái)連接各個(gè)參數(shù);
| 代碼 | 點(diǎn)評(píng) |
|---|---|
- (int)runModalForDirectory:(NSString *)path file:(NSString *)name types:(NSArray *)fileTypes; |
對(duì) |
- (int)runModalForDirectory:(NSString *)path andFile:(NSString *)name andTypes:(NSArray *)fileTypes; |
錯(cuò) |
雖然上面的例子中使用 and 看起來(lái)也不錯(cuò),但當(dāng)你的方法有許多參數(shù)時(shí),就會(huì)顯得有些繁瑣累贅不夠簡(jiǎn)潔。
- 如果方法描述兩種獨(dú)立的行為,使用 and 來(lái)串接它們。
| 代碼 | 點(diǎn)評(píng) |
|---|---|
- (BOOL)openFile:(NSString *)fullPath withApplication:(NSString *)appName andDeactivate:(BOOL)flag; |
NSWorkspace |
訪問(wèn)方法
訪問(wèn)方法是對(duì)象屬性的讀取與設(shè)置方法。其命名有特定的格式,依賴于屬性描述的內(nèi)容。
-
如果屬性是用名詞描述的,則命名格式為:
- (void)setNoun:(type)aNoun; - (type)noun;例如:
- (void)setgColor:(NSColor *)aColor; - (NSColor *)color; -
如果屬性是用形容詞描述的,則命名格式為:
- (void)setAdjective:(BOOL)flag; - (BOOL)isAdjective;例如:
- (void)setEditable:(BOOL)flag; - (BOOL)isEditable; -
如果屬性是用動(dòng)詞描述的,則命名格式為:(動(dòng)詞要用現(xiàn)在時(shí)時(shí)態(tài))
- (void)setVerbObject:(BOOL)flag; - (BOOL)verbObject;例如:
- (void)setShowAlpha:(BOOL)flag; - (BOOL)showsAlpha; 不要使用動(dòng)詞的過(guò)去分詞形式作形容詞使用;
| 代碼 | 點(diǎn)評(píng) |
|---|---|
- (void)setAcceptsGlyphInfo:(BOOL)flag; |
對(duì) |
- (BOOL)acceptsGlyphInfo; |
對(duì) |
- (void)setGlyphInfoAccepted:(BOOL)flag; |
錯(cuò) |
- (BOOL)glyphInfoAccepted; |
錯(cuò) |
- 可以使用情態(tài)動(dòng)詞(can/should/will等)來(lái)提高清晰性,但不要使用 do 或 does ;
| 代碼 | 點(diǎn)評(píng) |
|---|---|
- (void)setCanHide:(BOOL)flag; |
對(duì) |
- (BOOL)canHide; |
對(duì) |
- (void)setShouldCloseDocument:(BOOL)flag; |
對(duì) |
- (void)shouldCloseDocument; |
對(duì) |
- (void)setDoseAcceptGlyphInfo:(BOOL)flag; |
錯(cuò) |
- (BOOL)doseAcceptGlyphInfo; |
錯(cuò) |
- 只有在方法需要間接返回多個(gè)值的情況下,才使用 get ;
| 代碼 | 點(diǎn)評(píng) |
|---|---|
- (void)getLineDash:(float *)pattern count:(int *)count phase:(float *)phase; |
NSBezierPath |
像上面這樣的方法,在其實(shí)現(xiàn)里應(yīng)允許接受 NULL 作為其 in/out 參數(shù),以表示調(diào)用者對(duì)一個(gè)或多個(gè)返回值不感興趣。
委托方法
委托方法是那些在特定事件發(fā)生時(shí)可被對(duì)象調(diào)用,并聲明在對(duì)象的委托類(lèi)中的方法。它們有獨(dú)特的命名約定,這些命名約定同樣也適用于對(duì)象的數(shù)據(jù)源方法。
-
名稱(chēng)以標(biāo)示發(fā)送消息的對(duì)象的類(lèi)名開(kāi)頭,省略類(lèi)名的前綴并小寫(xiě)類(lèi)第一個(gè)字符;
- (BOOL)tableView:(NSTableView *)tableView shouldSelectRow:(int)row; - (BOOL)application:(NSApplication *)sender openFile:(NSString *)filename; -
冒號(hào)緊跟在類(lèi)名之后(隨后的那個(gè)參數(shù)表示委派的對(duì)象)。該規(guī)則不適用于只有一個(gè) sender 參數(shù)的方法;
- (BOOL)applicationOpenUntitledFile:(NSApplication *)sender; -
上面的那條規(guī)則也不適用于響應(yīng)通知的方法。在這種情況下,方法的唯一參數(shù)表示通知對(duì)象;
- (void)windowDidChangeScreen:(NSNotification *)notification; -
用于通知委托對(duì)象,操作即將發(fā)生或已經(jīng)發(fā)生的方法名中要使用 did 或 will ;
- (void)browserDidScroll:(NSBrowser *)sender; - (NSUndoManager *)windowWillReturnUndoManager:(NSWindow *)window; -
用于詢問(wèn)委托對(duì)象可否執(zhí)行某操作的方法名中可使用 did 或 will,但最好使用 should ;
- (BOOL)windowShouldClose:(id)sender;
集合方法
管理對(duì)象(集合中的對(duì)象被稱(chēng)之為元素)的集合類(lèi),約定要具備如下形式的方法:
- (void)addElement:(elementType)anObj;
- (void)removeElement:(elementType)anObj;
- (NSArray *)elements;
例如:
- (void)addLayoutManager:(NSLayoutManager *)anObj;
- (void)removeLayoutManager:(NSLayoutManager *)anObj;
- (NSArray *)layoutManagers;
集合方法命名有如下一些限制和約定:
如果集合中的元素?zé)o序,返回 NSSet,而不是 NSArray ;
-
如果將元素插入指定位置的功能很重要,則需具備如下方法:
- (void)insertElement:(elementType)anObj atIndex:(int)index; - (void)removeElementAtIndex:(int)index;
集合方法的實(shí)現(xiàn)要考慮如下細(xì)節(jié):
以上集合類(lèi)方法通常負(fù)責(zé)管理元素的所有者關(guān)系,在 add 或 insert 的實(shí)現(xiàn)代碼里會(huì) retain 元素,在 remove 的實(shí)現(xiàn)代碼中會(huì) release 元素;
-
當(dāng)被插入的對(duì)象需要持有指向集合對(duì)象的指針時(shí),通常使用 set… 來(lái)命名其設(shè)置該指針的方法,且不要 retain 集合對(duì)象。比如上面的 insertLayerManager:atIndex: 這種情形, NSLayoutManager 類(lèi)提供如下方法來(lái)獲取管理其的集合對(duì)象,并且是由集合對(duì)象在添加其時(shí)進(jìn)行調(diào)用:
- (void)setTextStorage:(NSTextStorage *)textStorage; - (NSTextStorage *)textStorage;
通常你不會(huì)直接調(diào)用 setTextStorage: ,而是覆寫(xiě)它。
另一個(gè)關(guān)于集合約定的例子來(lái)自 NSWindow 類(lèi):
- (void)addChildWindow:(NSWindow *)childWin ordered:(NSWindowOrderingMode)place;
- (void)removeChildWindow:(NSWindow *)childWin;
- (NSArray *)childWindows;
- (NSWindow *)parentWindow;
- (void)setParentWindow:(NSWindow *)window;
方法參數(shù)
命名方法參數(shù)時(shí)要考慮如下規(guī)則:
- 參數(shù)名小寫(xiě)第一個(gè)單詞的首字符,大寫(xiě)后繼單詞的首字符。如:
- (void)removeObject:(id)anObject;; - 不要在參數(shù)名中使用 pointer 或 ptr,讓參數(shù)的類(lèi)型來(lái)說(shuō)明它是指針;
- 避免使用 one, two, … 作為參數(shù)名;
- 避免為節(jié)省幾個(gè)字符而縮寫(xiě)。
按照 Cocoa 慣例,以下關(guān)鍵字與參數(shù)聯(lián)合使用:
…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
私有方法
大多數(shù)情況下,私有方法命名與公共方法命名約定相同,但通常我們約定給私有方法添加前綴,以便與公共方法區(qū)分開(kāi)來(lái)。即使這樣,私有方法的名稱(chēng)很容易 導(dǎo)致特別的問(wèn)題。當(dāng)你設(shè)計(jì)一個(gè)繼承自 Cocoa framework 某個(gè)類(lèi)的子類(lèi)時(shí),你無(wú)法知道你的私有方法是否不小心覆蓋了框架中基類(lèi)的同名方法。
Cocoa framework 的私有方法名稱(chēng)通常以下劃線作為前綴(如: _fooData ),以標(biāo)示其私有含義。基于這樣的事實(shí),遵循以下兩條建議:
- 不要使用下劃線作為你自己的私有方法名稱(chēng)的前綴, Apple 保留這種用法;
- 若要繼承 Cocoa framework 中一個(gè)超大的類(lèi)(如: NSView ),并且想要使你的私有方法名稱(chēng)與基類(lèi)中的區(qū)別開(kāi)來(lái),你可以為你的私有方法名稱(chēng)添加你自己的前綴。這個(gè)前綴應(yīng)該具有唯一性,如基于你 公司的名稱(chēng),或工程的名稱(chēng),并以“XX_”形式給出。比如你的工程名為”Byte Flogger”,那么就可以是“BF_addObject:”。
盡管為私有方法名稱(chēng)添加前綴的建議與前面類(lèi)中方法命名的約定沖突,但這里的意圖與之有所不同:為了防止不小心地覆蓋了基類(lèi)中的私有方法。
函數(shù)命名
Objective-C 允許通過(guò)函數(shù)( C 形式的函數(shù))描述行為,就如成員方法一樣。
函數(shù)命名應(yīng)該遵循如下幾條規(guī)則:
-
函數(shù)命名與成員方法命名相似,但有兩點(diǎn)不同:
- 它們有前綴,其前綴與你使用的類(lèi)和常量的前綴相同
- 大寫(xiě)前綴后緊跟的第一個(gè)單詞首字符
-
大多數(shù)函數(shù)名稱(chēng)以動(dòng)詞開(kāi)頭,這個(gè)動(dòng)詞描述該函數(shù)的行為。
NSHighlightRect NSDeallocateObject
查詢屬性的函數(shù)有個(gè)更多的規(guī)則要遵循:
-
查詢第一個(gè)參數(shù)的屬性的函數(shù),省略動(dòng)詞;
unsigned int NSEventMaskFromType(NSEventType type); float NSHeight(NSRect rect); -
返回值為引用的方法,使用 Get ;
const char *NSGetSizeAndAlignment(const char *typePtr, unsigned int *sizep, unsigned int *alignp); -
返回 boolean 值的函數(shù),名稱(chēng)使用判斷動(dòng)詞 is/does 開(kāi)頭。
BOOL NSDecimalIsNotANumber(const NSDecimal *decimal);
實(shí)例變量與數(shù)據(jù)類(lèi)型命名
這一節(jié)描述實(shí)例變量、常量、異常以及通知的命名約定。
實(shí)例變量
在為類(lèi)添加實(shí)例變量要注意如下幾點(diǎn):
- 避免創(chuàng)建 @public 的實(shí)例變量;
- 使用 @private, @protected 顯式限定實(shí)例變量的訪問(wèn)權(quán)限;
- 確保實(shí)例變量的名稱(chēng)簡(jiǎn)明扼要地描述了它所代表的屬性。
如果實(shí)例變量設(shè)計(jì)為可被訪問(wèn)的,確保編寫(xiě)了訪問(wèn)方法。
枚舉常量
使用枚舉來(lái)定義一組相關(guān)的整數(shù)常量;
-
枚舉常量與其 typedef 命名遵守函數(shù)命名規(guī)則。如:來(lái)自 NSMatrix.h 中的例子:(本例中的 typedef
<tag>,即 _NSMatrixMode 不是必須的);typedef enum _NSMatrixMode { NSRadioModeMatrix = 0, NSHighlightModeMatrix = 1, NSListModeMatrix = 2, NSTrackModeMatrix = 3 } NSMatrixMode; -
位掩碼常量可以使用匿名枚舉;
enum { NSBorderlessWindowMask = 0, NSTitledWindowMask = 1 << 0, NSClosableWindowMask = 1 << 1, NSMiniaturizableWindowMask = 1 << 2, NSResizableWindowMask = 1 << 3 };
const 常量
使用 const 來(lái)修飾浮點(diǎn)常量或彼此沒(méi)有關(guān)聯(lián)的整數(shù)常量,以及字符串常量等;
-
const 常量命名規(guī)則與函數(shù)命名規(guī)則相同。
const float NSLightGray;
字符串常量
通常不使用 #define 宏定義來(lái)定義字符串常量;
-
具體特殊使用場(chǎng)景的字符串常量,使用其含義來(lái)命名,命名約定與整數(shù)常量相同;
const NSString *SDApplicationName = @”SDMobileCRM”; -
一般場(chǎng)量下使用的字符串常量,且字符串內(nèi)容不含特殊符號(hào)的,則使用CS前綴后接字符串內(nèi)容來(lái)命名。并且名稱(chēng)中的大小寫(xiě)也與字符串內(nèi)容一致。這類(lèi)字符串常量通常會(huì)統(tǒng)一地放在一個(gè)公共的頭文件個(gè)用extern進(jìn)行聲明,并在另一個(gè)實(shí)現(xiàn)文件中進(jìn)行賦值:
const NSString *CSretcode = @”retcode”; const NSString *CSRetCode = @”RetCode”;
其他常量
通常不使用 #define 來(lái)創(chuàng)建常量。如上面所述,整數(shù)常量請(qǐng)使用枚舉,浮點(diǎn)數(shù)常量請(qǐng)使用 const ;
使用大寫(xiě)字母來(lái)定義預(yù)處理編譯宏。如:
#ifdef DEBUG;編譯器定義的宏名首尾都有雙下劃線。如:
__MACH__;-
為 notification 及 dictionary key 定義字符串常量,從而能夠利用編譯器的拼寫(xiě)檢查,減少書(shū)寫(xiě)錯(cuò)誤。 Cocoa 框架提供了很多這樣的范例:
APPKIT_EXTERN NSString *NSPrintCopies;
實(shí)際的字符串值在實(shí)現(xiàn)文件中賦予。(注意: APPKIT_EXTERN 宏等價(jià)于 Objective-C 中 extern 。)
異常與通知
異常與通知的命名遵循相似的規(guī)則,但是它們有各自推薦的使用模式。
異常
雖然你可以基于任何目的而使用異常(由 NSException 類(lèi)及相關(guān)類(lèi)實(shí)現(xiàn)),但 Cocoa 通常不使用異常來(lái)處理常規(guī)的,可預(yù)料的錯(cuò)誤。在這些情形下,使用諸如 nil/NULL/NO 或錯(cuò)誤代碼之類(lèi)的返回值。異常的典型應(yīng)用是類(lèi)似數(shù)組越界之類(lèi)的編程錯(cuò)誤。
異常由具有如下形式的全局 NSString 對(duì)象標(biāo)識(shí):
[Prefix] + UniquePartOfName + Exception
UniquePartOfName 部分是由連續(xù)的首字符大寫(xiě)的單詞組成。例如:
NSColorListIOException
NSColorListNotEditableException
NSDraggingException
NSFontUnavailableException
NSIllegalSelectorException
通知
如果一個(gè)類(lèi)有委托,那它的大部分通知可能由其委托的委托方法來(lái)處理。這些通知的名稱(chēng)應(yīng)該能夠反應(yīng)其響應(yīng)的委托方法。比如,當(dāng)應(yīng)用程序提交 NSApplicationDidBecomeActiveNotification 通知時(shí),全局 NSApplication 對(duì)象的委托對(duì)象會(huì)注冊(cè)對(duì)該通知的處理,從而能夠接收 applicaitonDidBecomeActive: 消息。
通知由具有如下形式的全局 NSString 對(duì)象標(biāo)識(shí):
[相關(guān)聯(lián)類(lèi)的名稱(chēng)] + [Did 或 Will] + [UniquePartOfName] + Notification
例如:
NSApplicationDidBecomeActiveNotification
NSWindowDidMiniaturizeNotification
NSTextViewDidChangeSelectionNotification
NSColorPanelColorDidChangeNotification
可接受的縮略名
在設(shè)計(jì)編程接口時(shí),通常名稱(chēng)不要縮寫(xiě)。然而,下面列出的縮寫(xiě)要么是固定下來(lái)的,要么是過(guò)去被廣泛使用的,所以你可以繼續(xù)使用。關(guān)于縮寫(xiě)有一些額外的注意事項(xiàng):
- 標(biāo)準(zhǔn) C 庫(kù)中長(zhǎng)期使用的縮寫(xiě)形式是可以接受的。如: alloc, getc ;
- 你可以在參數(shù)名中更自由地使用縮寫(xiě)。如: imageRep, col(column), obj, otherWin 。
常見(jiàn)的縮寫(xiě)
| 縮寫(xiě) | 含義 |
|---|---|
| alloc | Allocate |
| app | Application |
| calc | Calculate |
| dealloc | Deallocate |
| dict | Dictionary |
| err | Error |
| func | Function |
| horiz | Horizontal |
| info | Information |
| init | Initialize |
| max | Maximum |
| msg | Message |
| nib | Interface Builder archive |
| pboard | Pasteboard |
| rect | Rectangle |
| ret | Return |
| temp | Temporary |
| vert | Vertical |
常見(jiàn)的略寫(xiě)
ASCII, PDF, XML, HTML, URL, RTF, HTTP, TIFF,
JPG, GIF, LZW, ROM, RGB, CMYK, MIDI, FTP
編碼
花括號(hào)的使用
-
把左花括號(hào)放在前一條語(yǔ)句的末尾;
if (someExpression) { DoSomething(); } -
函數(shù)及方法的左花括號(hào)單獨(dú)放在一行;
void DoSomething() { // do something here. } -
把右花括號(hào)單獨(dú)放在一行,并且后面一般不再跟其它語(yǔ)句, do-while 語(yǔ)句除外;
if (someExpression) { DoSomething(); } else { DoSomethingElse(); } do { DoSomething(); } while (someExpression); -
不要省略花括號(hào),即使花括號(hào)內(nèi)只有一行語(yǔ)句;
錯(cuò)誤示范:
if (someExpression) DoSomething();正確示范:
if (someExpression) { DoSomething(); }
空格的使用
-
在左括號(hào)“(”及右括號(hào)“)”的前后均不保留空格,除非與之連接的語(yǔ)句元素有特殊的留空要求,此規(guī)則同樣適用于“@{”, “}”、“[”, “]”以及“<”, “>”;
result = 2 * (3 + 4); id object = NSAllocateObject(aClass, extraBytes, zone); NSDictionary *dict = @{@”key”: @”value”}; NSString *value = dict[@”key”]; self = [self init]; @interface MainViewController : UIViewController <UITableViewDataSource, UITableViewDelegate> -
在逗號(hào)“,”的前面不保留空格,后面保留一個(gè)空格,此規(guī)則同樣適用于字典中的冒號(hào)“:”和for語(yǔ)句中的分號(hào)“;”;
id object = NSAllocateObject(aClass, extraBytes, zone); NSDictionary *dict = @{@”key”: @”value”}; for (i = 0; i < 10; ++i) { printf(“%d\n”, i); } -
在運(yùn)算符的前后均保留一個(gè)空格,單目運(yùn)算符除外;
result = 2 * (3 + 4); result = (value >= 0) ? 1 : -1; -
在單目運(yùn)算符前后均不保留空格,除非與之連接的語(yǔ)句元素有特殊的留空要求;
result = !enable; result = [*someObject isEnable]; -
在“@”的前后均不保留空格,除非與之連接的語(yǔ)句元素有特殊的留空要求;
@interface MainViewController : UIViewController { @private int _value; } - (int)value; @end SEL action = @selector(onClick:); -
方法中的“+”、“-”,放在一行的開(kāi)始位置,并與后面的內(nèi)容保留一個(gè)空格;
+ (id)alloc; - (id)init; -
方法參數(shù)中的“:”,前后均不保留空格;
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context; -
指針聲明中的“”,前面保留一個(gè)空格,后面不保留*空格;
NSDictionary *dict = @{@”key”: @”value”}; const char *NSGetCString(NSString *string); - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context; -
關(guān)鍵字的前后如果有其它連接的內(nèi)容,則均保留一個(gè)空格,如: if, else, for, do, while, switch, return 等;
do { DoSomething(); } while (someExpression); 在其它沒(méi)有特別說(shuō)明的情況下,如果符號(hào)的前后與之連接的語(yǔ)句元素沒(méi)有特殊的留空要求,則均不保留空格;
行尾一般不建議留空格(在 xcode->Preferences->Text Editing 中勾上 Automatically trim trailing whitespace 及 Including whitespace-only lines )。
空行的使用
-
函數(shù)或方法(包括放在前面的注釋?zhuān)┑膶?shí)現(xiàn)前后均保留一空行;
// Show tips. void showTips(NSString *tips) { // show tips here. } // Make tips. void makeTips(NSError *error) { // make tips here. } -
函數(shù)或方法的聲明,如果使用了文檔注釋?zhuān)瑒t應(yīng)該在前后均保留一空行,否則可以不用;
/** * 響應(yīng)協(xié)議。 */ @protocol AuthenticationSystemDelegate <NSObject> @optional /** * 實(shí)名認(rèn)證系統(tǒng)。 * * @param cmias 實(shí)名認(rèn)證系統(tǒng)。 * @param applicationURLString 啟動(dòng)App的URL路徑。 */ - (void)authenticationSystem:(ChinaMobileIdentityAuthenticationSystem *)cmias beginInvoke:(NSString *)applicationURLString; /** * 已經(jīng)結(jié)束調(diào)用認(rèn)證系統(tǒng)。 * * @param cmias 實(shí)名認(rèn)證系統(tǒng)。 * @param userInfo 返回的用戶信息。 * * @note 調(diào)用失敗時(shí),userInfo僅包含失敗信息,沒(méi)有用戶信息。 */ - (void)chinaMobileIdentityAuthenticationSystem:(ChinaMobileIdentityAuthenticationSystem *)cmias endInvoke:(NSDictionary *)userInfo; @end -
邏輯代碼中的注釋前后均保留一空行,用以突出注釋內(nèi)容,及劃分代碼塊;
// 調(diào)用實(shí)名認(rèn)證系統(tǒng)。 - (void)invokeByBillid:(NSString *)billid account:(NSString *)account { // 先將入?yún)⒕彺嫫饋?lái),在調(diào)用createcheckorder服務(wù)成功后,拉起APP需要這些參數(shù)。 self.billid = billid; self.account = account; // 調(diào)用createcheckorder服務(wù),獲取拉起APP所需要的參數(shù)。 self.CRMService.action = @selector(createCheckOrderSuccess:); self.CRMService.errorAction = @selector(createCheckOrderFail:); [self.CRMService request10085CreateCheckOrder:billid account:account]; } 文件末尾保留一空行。
縮進(jìn)的使用
不要使用制表符(即tab鍵),而是用4個(gè)連續(xù)的空格來(lái)進(jìn)行縮進(jìn);
-
成員變量中訪問(wèn)限制修飾 @public, @package, @protected, @private 與行首保留兩個(gè)空格。
@interface MainViewController : UIViewController { @private int _value; } @end
折行的使用
-
方法的聲明/調(diào)用中參數(shù)太多,而無(wú)法放在一行上,應(yīng)該對(duì)每個(gè)參數(shù)進(jìn)行折行,并以冒號(hào)“:”對(duì)齊;
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context; [self invokeByTransactionid:transactionid billid:self.billid account:self.account channelid:channelid provincecode:provincecode signature:signature];如果第一行的長(zhǎng)度較短,而令其它行無(wú)法以冒號(hào)“:”對(duì)齊,則無(wú)法對(duì)齊的行以左對(duì)齊方式對(duì)齊,其它行仍以冒號(hào)“:”對(duì)齊。(剪切這部份代碼,再粘貼時(shí),xcode會(huì)自動(dòng)完成這個(gè)工作。)
- (void)application:(UIApplication *)application observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context; -
函數(shù)的聲明/調(diào)用中參數(shù)太多,而無(wú)法放在一行上,應(yīng)該對(duì)每個(gè)參數(shù)進(jìn)行折行,并與第一個(gè)參數(shù)進(jìn)行左對(duì)齊。
NSCalculationError NSDecimalAdd(NSDecimal *result, const NSDecimal *leftOperand, const NSDecimal *rightOperand, NSRoundingMode roundingMode); NSDecimalAdd(&result, &leftOperand, &rightOperand, roundingMode);
注釋
文檔注釋
-
類(lèi)、函數(shù)、方法等的文檔注釋采用業(yè)內(nèi)流行的javadoc風(fēng)格格式;
/** * 與“實(shí)名認(rèn)證系統(tǒng)“對(duì)接的出入口。 */ @interface ChinaMobileIdentityAuthenticationSystem : NSObject /** * 該類(lèi)使用單例模式,這里返回其引用。 * * @return 該類(lèi)使用單例模式,這里返回其引用。 */ + (instancetype)sharedInstance; /** * 調(diào)用實(shí)名認(rèn)證系統(tǒng)。 * * @param billid 業(yè)務(wù)號(hào)碼 * @param account 操作員工號(hào) */ - (void)invokeByBillid:(NSString *)billid account:(NSString *)account; /** * 調(diào)用實(shí)名認(rèn)證系統(tǒng)。 * * @param transactionid 全網(wǎng)唯一操作流水號(hào) * @param billid 業(yè)務(wù)號(hào)碼 * @param account 操作員工號(hào) * @param channelid 渠道ID * @param provincecode 省編號(hào) * @param signature 簽名 */ - (void)invokeByTransactionid:(NSString *)transactionid billid:(NSString *)billid account:(NSString *)account channelid:(NSString *)channelid provincecode:(NSString *)provincecode signature:(NSString *)signature; /** * 接收實(shí)名認(rèn)證系統(tǒng)返回信息。 * * @param message 返回信息 * * @note 一般是在UIApplicationDelegate的application:openURL:sourceApplication:annotation:方法中調(diào)用。 */ - (void)receiveReturnMessage:(NSString *)message; @end
xcode中可以使用擴(kuò)展插件 VVDocumenter 自動(dòng)生成文檔注釋?zhuān)恍枰谏勺⑨尩牡胤桨慈齻€(gè)斜杠///。
下載編譯后會(huì)自動(dòng)安裝為xcode的插件,重啟xcode后可以看到 Window->VVDocumenter 的配置菜單
在需要生成注釋的地方按三個(gè)斜杠///,會(huì)自動(dòng)生成文檔注釋
-
僅在函數(shù)、方法的聲明中使用文檔注釋?zhuān)诤瘮?shù)、方法的實(shí)現(xiàn)中僅使用單行注釋?zhuān)?/p>
@implementation ChinaMobileIdentityAuthenticationSystem // 該類(lèi)使用單例模式,這里返回其引用。 + (instancetype)sharedInstance { static id instance = nil; if (!instance) { instance = [[ChinaMobileIdentityAuthenticationSystem alloc] init]; } return instance; } // 調(diào)用實(shí)名認(rèn)證系統(tǒng)。 - (void)invokeByBillid:(NSString *)billid account:(NSString *)account { … } // 調(diào)用實(shí)名認(rèn)證系統(tǒng)。 - (void)invokeByTransactionid:(NSString *)transactionid billid:(NSString *)billid account:(NSString *)account channelid:(NSString *)channelid provincecode:(NSString *)provincecode signature:(NSString *)signature { … } // 接收實(shí)名認(rèn)證系統(tǒng)返回信息。 - (void)receiveReturnMessage:(NSString *)message { … } @end 重寫(xiě)父類(lèi)中的方法,可以不用撰寫(xiě)文檔注釋?zhuān)鏸nit等;
實(shí)現(xiàn)委托協(xié)議中的方法,可以不用撰寫(xiě)文檔注釋?zhuān)瑑H在協(xié)議定義的地方撰寫(xiě)文檔注釋。
代碼注釋
-
屬性/變量聲明的注釋?zhuān)绻^短,可放在變量聲明的后面,并與相鄰幾行屬性/變量聲明的注釋對(duì)齊;
@interface IndicatorModel : NSObject @property (nonatomic, copy) NSString *cityname; // 地市名稱(chēng),默認(rèn)為天津。 @property (nonatomic, copy) NSString *updatedate; // 指標(biāo)數(shù)據(jù)截止日期,格式為yyyy-MM-dd。 @property (nonatomic, copy) NSString *periodcode; // 指標(biāo)周期類(lèi)型,D-日,M-月,Y-年。 @property (nonatomic, copy) NSString *jfindcode; // 經(jīng)分指標(biāo)編號(hào)。 @property (nonatomic, copy) NSString *jfindname; // 經(jīng)分指標(biāo)名稱(chēng)。 @property (nonatomic, copy) NSString *value; // 指標(biāo)值。 @property (nonatomic, copy) NSString *rate; // 比例變動(dòng),百分比值,不帶百分號(hào)。 @property (nonatomic, copy) NSString *ratetype; // 比例變動(dòng)類(lèi)型,1-同比,2-環(huán)比。 @property (nonatomic, copy) NSString *unit; // 單位,比如:萬(wàn)元。 @property (nonatomic, copy) NSString *ranking; // 指標(biāo)排名。 @property (nonatomic, copy) NSString *rankingchange; // 指標(biāo)排名變動(dòng),正數(shù)為排名上升,負(fù)數(shù)為排名下降,排名不變則為零。 @property (nonatomic, copy) NSString *picurl; // 指標(biāo)圖片地址,暫時(shí)未使用。 @property (nonatomic, copy) NSString *sortorder; // 指標(biāo)排序,暫時(shí)使用。 @end 屬性/變量聲明的注釋?zhuān)绻^長(zhǎng),則應(yīng)該放在聲明的前面,并緊貼著聲明,這樣的屬性/變量聲明應(yīng)該在前后均保留一空行;
代碼的注釋不應(yīng)該用于描述顯而易見(jiàn)的信息;
-
代碼的注釋?xiě)?yīng)該言而有物,不能僅描述表面內(nèi)容;
錯(cuò)誤示范:
// 將入?yún)⒕彺嫫饋?lái)。 self.billid = billid; self.account = account;正確示范:
// 先將入?yún)⒕彺嫫饋?lái),在調(diào)用createcheckorder服務(wù)成功后,拉起APP需要這些參數(shù)。 self.billid = billid; self.account = account; -
僅通過(guò)代碼無(wú)法獲知的信息應(yīng)該進(jìn)行詳細(xì)的注釋?zhuān)绯鋈雲(yún)⒌娜≈?、格式等?/p>
@property (nonatomic, copy) NSString *updatedate; // 指標(biāo)數(shù)據(jù)截止日期,格式為yyyy-MM-dd。 @property (nonatomic, copy) NSString *periodcode; // 指標(biāo)周期類(lèi)型,D-日,M-月,Y-年。 if (![SDUtility IsServiceBadBack:result]) { NSDictionary *tagset = result[RES_BODY][CRM_10085_CREATECHECKORDER_RESP][WORKBENCH_COMMON_TAGSET]; NSString *retcode = tagset[CRM_10085_CREATECHECKORDER_RETCODE]; // 根據(jù)返回信息中的返回碼判斷調(diào)用是否成功。 // 其中retcode = 100 - 成功; // retcode = -1 - 代理商號(hào)碼不合法(河北無(wú)此場(chǎng)景); // retcode = -2 - 用戶號(hào)碼不合法; // retcode = -3 - 代理商渠道歸屬的分公司與開(kāi)戶手機(jī)號(hào)碼歸屬分公司不一致。 if (![retcode isEqualToString:@”100”]) {
約定俗成的習(xí)慣
成員變量定義
- 不建議使用成員變量,應(yīng)使用屬性來(lái)代替成員變量。如果是私有成員變量,可在類(lèi)的私有類(lèi)別定義中進(jìn)行聲明。
屬性定義
-
字符串NSString類(lèi)型的屬性,應(yīng)該使用copy修飾。避免使用strong/retain等引用的方式時(shí),用戶賦值NSMutableString類(lèi)型的對(duì)象后,又對(duì)字符串內(nèi)容進(jìn)行了修改,導(dǎo)致不可預(yù)料的錯(cuò)誤;
@property (nonatomic, copy) NSString *cityname; -
用作委托對(duì)象的屬性,應(yīng)該使用weak/assign修飾;
@property (nonatomic, weak) id<UIMyDelegate> delegate;
比較操作
-
永遠(yuǎn)不要對(duì)YES進(jìn)行“==”、“!=”的比較;
錯(cuò)誤示范:
if (YES == enable) { … }正確示范:
if (enable) { … } -
沒(méi)必要對(duì)nil/NULL/NO進(jìn)行“==”、“!=”的比較;
錯(cuò)誤示范:
if (nil != object) { … }正確示范:
if (object) { … }