[Cocoa][译]苹果 Cocoa 编码规范中文版
官方文档:
Coding Guidelines for Cocoa
> Code Naming Basics 代码命名基础
在面向对象软件库的设计过程中,开发人员经常忽视对类,方法,函数,常量以及其他编程接口元素的命名。本节讨论大多数Cocoa接口的一些命名约定。
>> General Principles 一般性原则
>>> Clarity 清晰性
● 最好是既清晰又简短,但不要为简短而丧失清晰性
代码 | 点评 |
destinationSelection | good |
destSel | 不清晰 |
setBackgroundColor: | good |
setBkgdColor: | 不清晰 |
你可能会认为某个缩写广为人知,但有可能并非如此,尤其是当你的代码被来自不同文化和语言背景的开发人员所使用时。
● 然而,你可以使用少数非常常见,历史悠久的缩写。请参考:”可接受的缩略名“一节
● 避免使用有歧义的 API 名称,如那些能被理解成多种意思的方法名称
代码 | 点评 |
- (int) tag | 在 NSView, NSCell, NSControl 中有定义 |
- (void) setStringValue:(NSString *) | 在许多 Cocoa classes 中有定义 |
请参考“方法参数”一节。
>>> No Self Reference 不要自我指涉
● 不要名称自我指涉
代码 | 点评 |
NSUnderlineByWordMask | okey |
NSTableViewColumnDidMoveNotification | okey |
>> Prefixes 前缀
前缀是名称的重要组成部分。它们可以区分软件的功能范畴。通常,软件会被打包成一个框架或多个紧密相关的框架(如 Foundation 和 Application Kit 框架)。前缀可以防止第三方开发者与苹果公司之间的命名冲突(同样也可防止苹果内部不同框架之间的命名冲突)
● 前缀有规定的格式。它由两到三个大写字符组成,不能使用下划线与子前缀
代码 | 点评 |
NSLocking | good |
NSLock | 糟糕,它看起来像类名 |
● 有些协议组合一些彼此无关的方法(这样做是避免创建多个独立的小协议)。这样的协议倾向于与某个类关联在一起,该类是协议的主要体现者。在这种情形,我们约定协议的名称与该类同名。NSObject 协议就是这样一个例子。这个协议组合一组彼此无关的方法,有用于查询对象在其类层次中位置的方法,有使之能调用特殊方法的方法以及用于增减引用计数的方法。由于 NSObject 是这些方法的主要体现者,所以我们用类的名称命名这个协议。
>> Header Files 头文件
头文件的命名方式很重要,我们可以根据其命名知晓头文件的内容。
● 声明孤立的类或协议:将孤立的类或协议声明放置在单独的头文件中,该头文件名称与类或协议同名
头文件 | 声明 |
NSString.h | NSString 和 NSMutableString 类 |
NSLock.h | NSLocking 协议和 NSLock, NSConditionLock, NSRecursiveLock 类 |
● 包含框架头文件:每个框架应该包含一个与框架同名的头文件,该头文件包含该框架所有公开的头文件。
- (NSSize) cellSize; | 对 |
- (NSSize) calcCellSize; | 错 |
- (NSSize) getCellSize; | 错 |
● 参数要用描述该参数的关键字命名
- (id) viewWithTag:(int)aTag; | 对 |
- (id) taggedView:(int)aTag; | 错 |
● 细化基类中的已有方法:创建一个新方法,其名称是在被细化方法名称后面追加参数关键词
- (int) runModalForDirectory:(NSString *)path file:(NSString *)name types:(NSArray *)fileTypes; | 对 |
- (int) runModalForDirectory:(NSString *)path addFile:(NSString *)name addTypes:(NSArray *)fileTypes; | 错 |
虽然上面的例子中使用 add 看起来也不错,但当你方法有太多参数关键字时就有问题。
● 如果方法描述两种独立的行为,使用 and 来串接它们
- (void) setAcceptsGlyphInfo:(BOOL)flag; | 对 |
- (BOOL) acceptsGlyphInfo; | 对 |
- (void) setGlyphInfoAccepted:(BOOL)flag; | 错 |
- (BOOL) glyphInfoAccepted; | 错 |
● 可以使用情态动词(can, should, will 等)来提高清晰性,但不要使用 do 或 does
- (void) getLineDash:(float *)pattern count:(int *)count phase:(float *)phase; | NSBezierPath |
像上面这样的方法,在其实现里应允许接受 NULL 作为其 in/out 参数,以表示调用者对一个或多个返回值不感兴趣。
>> Delegate Methods 委托方法
委托方法是那些在特定事件发生时可被对象调用,并声明在对象的委托类中的方法。它们有独特的命名约定,这些命名约定同样也适用于对象的数据源方法。
● 名称以标示发送消息的对象的类名开头,省略类名的前缀并小写类第一个字符
- (BOOL) applicationOpenUntitledFile:(NSApplication *)sender; |
● 上面的那条规则也不适用于响应通知的方法。在这种情况下,方法的唯一参数表示通知对象
- (void) browserDidScroll:(NSBrowser *)sender; |
- (NSUndoManager *) windowWillReturnUndoManager:(NSWindow *)window; |
● 用于询问委托对象可否执行某操作的方法名中可使用 did 或 will,但最好使用 should
- (void) addElement:(elementType)adObj; |
- (void) removeElement:(elementType)anObj; |
- (NSArray *)elements; |
例如:
- (void) insertElement:(elementType)anObj atIndex:(int)index; |
- (void) removeElementAtIndex:(int)index; |
集合方法的实现要考虑如下细节:
● 以上集合类方法通常负责管理元素的所有者关系,在 add 或 insert 的实现代码里会 retain 元素,在 remove 的实现代码中会 release 元素
● 当被插入的对象需要持有指向集合对象的指针时,通常使用 set... 来命名其设置该指针的方法,且不要 retain 集合对象。比如上面的 insertLayerManager:atIndex: 这种情形,NSLayoutManager 类使用如下方法:
- (void) addChildWindow:(NSWindow *)childWin ordered:(NSWindowOrderingMode)place; |
- (void) removeChildWindow:(NSWindow *)childWin; |
- (NSArray *)childWindows; |
- (NSWindow *) parentWindow; |
- (void) setParentWindow:(NSWindow *)window; |
>> Method Arguments 方法参数
命名方法参数时要考虑如下规则:
● 如同方法名,参数名小写第一个单词的首字符,大写后继单词的首字符。如:removeObject:(id)anObject
● 不要在参数名中使用 pointer 或 ptr,让参数的类型来说明它是指针
● 避免使用 one, two,...,作为参数名
● 避免为节省几个字符而缩写
按照 Cocoa 惯例,以下关键字与参数联合使用:
NSHighlightRect |
NSDeallocateObject |
查询属性的函数有个更多的规则要遵循:
● 查询第一个参数的属性的函数,省略动词
const char *NSGetSizeAndAlignment(const char *typePtr, unsigned int *sizep, unsigned int *alignp) |
● 返回 boolean 值的函数,名称使用判断动词 is/does 开头
const float NSLightGray; |
>>> 其他常量
● 通常不使用 #define 来创建常量。如上面所述,整数常量请使用枚举,浮点数常量请使用 const
● 使用大写字母来定义预处理编译宏。如:#ifdef DEBUG
● 编译器定义的宏名首尾都有双下划线。如:__MACH__
● 为 notification 名及 dictionary key 定义字符串常量,从而能够利用编译器的拼写检查,减少书写错误。Cocoa 框架提供了很多这样的范例:
NSColorListIOException |
NSColorListNotEditableException |
NSDraggingException |
NSFontUnavailableException |
NSIllegalSelectorException |
>>> 通知
如果一个类有委托,那它的大部分通知可能由其委托的委托方法来处理。这些通知的名称应该能够反应其响应的委托方法。比如,当应用程序提交 NSApplicationDidBecomeActiveNotification 通知时,全局 NSApplication 对象的委托会注册从而能够接收 applicaitonDidBecomeActive: 消息。
通知由具有如下形式的全局 NSString 对象标识:
[相关联类的名称] + [Did 或 Will] + [UniquePartOfName] + Notification
例如:
缩写 | 含义 | 缩写 | 含义 |
alloc | Allocate | msg | Message |
alt | Alternate | nib | Interface Builder archive |
app | Application | pboard | Pasteboard |
calc | Calculate | rect | Rectangle |
dealloc | Deallocate | Rep | Representation |
func | Function | temp | Temporary |
horiz | Horizontal | vert | Vertical |
info | Information | init | Initialize |
max | Maximum |
常见的缩写:
<table xhe-border"="" style="font-family: Arial, Verdana, sans-serif; font-size: 12px; border-top-color: #d3d3d3; border-right-color: #d3d3d3; border-bottom-color: #d3d3d3; border-left-color: #d3d3d3; " cellpadding="0" cellspacing="0" width="398.0">
ASCII,PDF,XML,HTML,URL,RTF,HTTP,TIFF
JPG,GIF,LZW,ROM,RGB,CMYK,MIDI,FTP
> 框架开发者小贴士与技巧
>> Initialize 初始化
>>> 类初始化
在 initialize 类方法中,能够编写实现一些延迟执行且只被一次的代码,initialize 类方法是由运行时系统在该类响应任何其他消息之前调用的。典型的应用是在其中设置类的版本信息。运行时系统向每个类发送 initialize 消息,即使该类没有实现 initialize,也会调用其基类的某个 initialize 方法。因此一个类的 initialize 方法可能会因为存在继承类的缘故被执行多次。因此有必要使用一定的技巧来防止只执行一次的代码被多次执行。如:NSFoo 类的 initialize 方法实现可能如下:
+ (id) initialize
{
if (self == [NSFoo class])
{
// 初始化代码
}
return self;
}
不应当显式调用 initialize 方法。如果你需要激活 initialize 方法,使用 [NSFoo self] 形式的调用。