[iOS譯文]Cocoa編碼規范
原文 http://www.cnblogs.com/yangfaxian/p/4673894.html
1.通用原則
1.1清晰
- 盡量清晰又簡潔,無法兩全時清晰更重要
Code |
Commentary |
insertObject:atIndex: |
√ |
insert:at: |
不清晰,insert什么?at表示什么? |
removeObjectAtIndex: |
√ |
removeObject: |
√ ,因為參數指明了移除對象 |
remove: |
不清晰,要移除什么? |
- 通常不應縮寫名稱,即使方法名很長也應完整拼寫
? 你可能認為某個縮寫眾所周知,但其實未必,特別是你周圍的開發者語言文化背景不同時
? 有一些歷史悠久的縮寫還是可以使用的,詳見第五章
Code |
Commentary |
destinationSelection |
√ |
destSel |
不清晰 |
setBackgroundColor: |
√ |
setBkgdColor: |
不清晰 |
- API命名避免歧義,例如一個方法名有多種理解
Code |
Commentary |
sendPort |
發送還是返回一個port ? |
displayName |
顯示一個名字還是返回UI界面上的標題? |
1.2一致
- 盡力保持Cocoa編程接口命名一致
? 如果有疑惑,請瀏覽當前頭文件或者參考文檔
- 當某個類的方法使用了多態時,一致性尤其重要
? 不同類里,功能相同的方法命名也應相同
Code |
Commentary |
- (NSInteger)tag |
定義在 NSView, NSCell, NSControl. |
- (void)setStringValue:(NSString *) |
定義在許多Cocoa類里 |
1.3 避免自引用(self Reference)
- 命名不應自引用
? 這里的自引用指的是在詞尾引用自身
Code |
Commentary |
NSString |
√ |
NSStringObject |
自引用,所有NSString都是對象,不用額外聲明 |
- Mask與Notification忽略此規則
Code |
Commentary |
NSUnderlineByWordMask |
√ |
NSTableViewColumnDidMoveNotification |
√ |
2.前綴
前綴是編程接口命名的重要部分,它們區分了軟件的不同功能區域:
- 前綴可以防止第三方開發者與Apple的命名沖突
? 同樣可以防止Apple內部的命名沖突
- 前綴有指定格式
? 它由二到三個大寫字母組成,不使用下劃線和子前綴
- 命名類、協議、函數、常量和typedef結構體時使用前綴
? 方法名不使用前綴(因為它存在于特定類的命名空間中)
? 結構體字段不使用前綴
Prefix |
Cocoa Framework |
NS |
Foundation |
NS |
Application Kit |
AB |
Address Book |
IB |
Interface Builder |
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區分開來
Code |
Commentary |
NSLocking |
√ |
NSLock |
不好,看起來像是一個class |
- 關聯class的protocol
? 一些protoco聚集了一堆無關方法,并試圖與某個class關聯在一起,由這個class來主導
? 這種protocol與class同名
? 如NSObject protocol
5.頭文件
頭文件的命名極其重要,因為它可以指出頭文件包含的內容:
- 聲明一個孤立的class或protocol
? 將聲明放入單獨的文件
? 使頭文件名與聲明的class/protocol相同
Header file |
Declares |
NSLocale.h |
包含NSLocale class. |
- 聲明關聯的class或protocol
? 將關聯的聲明(class/category/protocol)放入同一個頭文件
? 頭文件名與主要的class/category/protocol相同
Header file |
Declares |
NSString.h |
包含NSString和NSMutableString classes. |
NSLock.h |
包含NSLocking protocol 和 NSLock, NSConditionLock, NSRecursiveLock classes. |
- Framework 頭文件
? 每個framework都應該有一個同名頭文件
? Include了框架內其他所有頭文件
Header file |
Framework |
Foundation.h |
Foundation.framework. |
- 添加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區分)
- (NSSize)cellSize; |
√ |
- (NSSize)calcCellSize; |
? |
- (NSSize)getCellSize; |
? |
- 在所有參數之前使用關鍵字
- (void)sendAction:(SEL)aSelector toObject:(id)anObject forAllCells:(BOOL)flag; |
√ |
- (void)sendAction:(SEL)aSelector :(id)anObject :(BOOL)flag; |
? |
- 確保參數之前的關鍵字充分描述了參數
- (id)viewWithTag:(NSInteger)aTag; |
√ |
- (id)taggedView:(int)aTag; |
? |
- 創建自定義 init 方法時,記得指明關聯的元素
- (id)initWithFrame:(CGRect)frameRect; |
√ |
- (id)initWithFrame:(NSRect)frameRect mode:(int)aMode cellClass:(Class)factoryId numberOfRows:(int)rowsHigh numberOfColumns:(int)colsWide; |
√ |
- 不要使用 and 來連接作為接收者屬性的關鍵字
? 雖然下面的例子使用 and 看似不錯,但是一旦參數非常多時就容易出現問題
- (int)runModalForDirectory:(NSString *)path file:(NSString *)name types:(NSArray *)fileTypes; |
√ |
- (int)runModalForDirectory:(NSString *)path andFile:(NSString *)name andTypes:(NSArray *)fileTypes; |
? |
- 除非方法描述了兩個獨立的操作,才使用 and 來連接它們
- (BOOL)openFile:(NSString *)fullPath withApplication:(NSString *)appName andDeactivate:(BOOL)flag; |
√ |
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;
- 不要把動詞的過去分詞形式當作形容詞使用
- (void)setAcceptsGlyphInfo:(BOOL)flag; |
√ |
- (BOOL)acceptsGlyphInfo; |
√ |
- (void)setGlyphInfoAccepted:(BOOL)flag; |
? |
- (BOOL)glyphInfoAccepted; |
? |
- 你可能使用情態動詞(can、should、will等)來增加可讀性,不過不要使用 do或 does
- (void)setCanHide:(BOOL)flag; |
√ |
- (BOOL)canHide; |
√ |
- (void)setShouldCloseDocument:(BOOL)flag; |
√ |
- (BOOL)shouldCloseDocument; |
√ |
- (void)setDoesAcceptGlyphInfo:(BOOL)flag; |
? |
- (BOOL)doesAcceptGlyphInfo; |
? |
- 只有方法需要間接返回多個值的情況下才使用 get
? 像這種接收多個參數的方法應該能夠傳入nil,因為調用者未必對每個參數都感興趣
- (void)getLineDash:(float *)pattern count:(int *)count phase:(float *)phase; |
NSBezierPath. |
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
縮寫 |
全稱 |
alloc |
Allocate. |
alt |
Alternate. |
app |
Application. |
calc |
Calculate. |
dealloc |
Deallocate. |
func |
Function. |
horiz |
Horizontal. |
info |
Information. |
init |
Initialize. |
int |
Integer |
max |
Maximum. |
min |
Minimum. |
msg |
Message. |
nib |
Interface Builder archive. |
pboard |
Pasteboard. |
rect |
Rectangle. |
Rep |
Representation. |
temp |
Temporary. |
vert |
Vertical. |
縮寫 |
全稱 |
ASCII |
American Standard Code for Information Interchange |
|
Portable Document Format |
XML |
Extensible Markup Language |
HTML |
HyperText Markup Language |
URL |
Uniform Resource Locator |
RTF |
Rich Text Format |
HTTP |
HyperText Transfer Protocol |
TIFF |
Tagged Image File Format |
JPG/JPEG |
Joint Photographic Experts GROUP |
PNG |
Portable Network Graphic Format |
GIF |
Graphics Interchange Format |
LZW |
Lempel Ziv Welch |
ROM |
Read-Only Memory |
RGB |
R(red)、G(green)、B(blue) |
CMYK |
C:Cyan、M:Magenta、Y:Yellow、K:Key Plate(blacK) |
MIDI |
Musical Instrument Digital Interface |
FTP |
File Transfer Protocol |