Coding GuideLines For Cocoa
###基本命名规范
####通用原则
#####清晰
- 命名应做到既清晰又简洁,两者权衡更注重于清晰
Code | Commentary |
---|---|
insertObject:atIndex: | Good |
insert:at: | 不清晰,插入什么的东西,at表示什么 |
removeObjectAtIndex: | Good |
removeObject: | Good,清晰的指出了要移除参数中的对象 |
remove: | 不清晰,什么被移除? |
- 通常情况下不要缩写单词,即使很长
Code | Commentary |
---|---|
destinationSelection | Good |
destSel | 不清晰 |
setBackgroundColor: | Good |
setBkgdColor: | 不清晰 |
- 少数通用的缩写可以使用
缩写 | 全称 |
---|---|
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 (used in class name such as NSBitmapImageRep). |
temp | Temporary. |
vert | Vertical. |
- 避免使用有歧义的API名称,例如有的方法名不止一种解释
Code | Commentary |
---|---|
sendPort | Does it send the port or return it? |
displayName | Does it display a name or return the receiver’s title in the user interface? |
#####一致性
- 命名要和
Cocoa
框架保持一致,如果不能确定,可以参考Cocoa
框架的头文件 - 当一个类使用多态性的便利时,一致性就特别重要。在不同类中做了相同事情的方法,方法名应该相同
Code | Commentary |
---|---|
- (NSInteger)tag | Defined in NSView, NSCell, NSControl. |
- (void)setStringValue:(NSString *) | Defined in a number of Cocoa classes. |
#####不要自我参照
- 命名不能自我参照
Code | Commentary |
---|---|
NSString | Okay. |
NSStringObject | Self-referential. |
- 作为掩码使用的常量和通知名称可以不遵守这条规则
Code | Commentary |
---|---|
NSUnderlineByWordMask | Okay. |
NSTableViewColumnDidMoveNotification | Okay. |
####前缀
- 前缀一般有俩个或三个大写字母构成,不能使用下划线和子前缀
Prefix | Cocoa Framework |
---|---|
NS | Foundation |
NS | Application Kit |
AB | Address Book |
IB | Interface Builder |
- 类名、协议名、常量、结构体、类别方法需要加前缀,普通方法名、结构体域不需要加前缀
####命名约定
-
由多个单词组成的名称,不要使用标点符号作为名称的一部分或者单词间的分割点(例如下划线、破折号),应该使用驼峰命名法,单词的首字母大写,单词之间构成一个整体来解释名称含义
-
对于方法名称,第一个单词首字母小写,其它嵌入的单词首字母大写,不要使用前缀。
fileExistsAtPath:isDirectory:
- 一些广泛使用的单词缩写首字母可以大写,例如
TIFFRepresentation
-
部分常用单词缩写
- ASCII
- XML
- HTML
- URL
- RTF
- HTTP
- TIFF
- JPG
- PNG
- GIF
- LZW
- ROM
- RGB
- CMYK
- MIDI
- FTP
-
对于函数名和常量名,相关联的类要使用相同的前缀,嵌入的单词首字母大写
-
NSRunAlertPanel NSCellDisabled
-
-
-
不要使用下划线作为私有方法名的前缀,苹果保留了这种用法,第三方框架使用下划线作为前缀,容易和苹果的私有方法冲突
####类名和协议名
类名需要包含一个名词来清楚的表示这个类是做什么的,类名还需要有一个合适的前缀。
协议名取决于协议怎么分组其行为方法的
- 大部分的协议组织了一组相关的方法,这些方法与特定的类没有关系。这种类型的协议名称不要与类名混淆,通用的解决办法是使用动名词(“ing”)
Code | Commentary |
---|---|
NSLocking | Good |
NSLock | Poor (seems like a name for a class). |
-
一些协议组织了一些不相关的方法,这些协议和某一个紧密的类相关联,这个类提供了协议最主要的实现。在这种情况下,类名和协议名相同。
NSObject
协议就是一个典型的例子
####头文件
怎样命名头文件非常重要,一些约定俗成的命名方式可以表示这个文件中包含什么。
- 声明单独的类和协议. 如果一个类或协议是独立的,则把这个类或协议的声明放到一个单独的头文件中,文件名和类或协议名相同
Header file | Declares |
---|---|
NSLocale.h | The NSLocale class. |
- 声明相关的类和协议。 一组相关联的类、分类、协议的声明放到同一个头文件中
Header file | Declares |
---|---|
NSString.h | NSString and NSMutableString classes. |
NSLock.h | NSLocking protocol and NSLock, NSConditionLock, and NSRecursiveLock classes. |
- 框架头文件。每个框架都应该有一个与框架名相同的头文件,这个文件包含了框架中所有公开的头文件
Header file | Framework |
---|---|
Foundation.h | Foundation.framework. |
- 在其他框架中为一个类添加API. 新的头文件名是在原类名后添加Additions,如NSBundleAdditions.h
- 若有一系列相关的方法,常量,结构体等,将其声明在一个合适名称的头文件里,如NSGraphics.h (Application Kit)
###方法命名规范
####通用规则
-
方法名首字母小写,嵌入的单词首字母大写,不要使用前缀。
有俩种特殊的场景不用遵守这条规则,一种是方法名的首个单词是通用的缩写(TIFF/PDF), 另外一种是组织和区分私有方法的时候可能会使用前缀。
-
表示对象动作的方法,以动词开始
- (void)invokeWithTarget:(id)target; - (void)selectTabViewItem:(NSTabViewItem *)tabViewItem
不要使用
do
或者does
之类的辅助动词,基本不会增加额外的含义。也不要用副词和形容词在动词前面修饰。 -
如果方法返回的是接收者的属性,方法名用属性名来命名,不用使用
get
, 除非间接返回多个值
- (NSSize)cellSize; | Right. |
---|---|
- (NSSize)calcCellSize; | Wrong. |
- (NSSize)getCellSize; | Wrong. |
- 在所有的参数前都使用关键字
- (void)sendAction:(SEL)aSelector toObject:(id)anObject forAllCells:(BOOL)flag; | Right. |
---|---|
- (void)sendAction:(SEL)aSelector :(id)anObject :(BOOL)flag; | Wrong. |
- 在参数前的单词要描述参数
- (id)viewWithTag:(NSInteger)aTag; | Right |
---|---|
- (id)taggedView:(int)aTag; | Wrong. |
- 对继承的方法扩展时,可以在原方法名后加关键字
- (id)initWithFrame:(CGRect)frameRect; | NSView, UIView. |
---|---|
- (id)initWithFrame:(NSRect)frameRect mode:(int)aMode cellClass:(Class)factoryId numberOfRows:(int)rowsHigh numberOfColumns:(int)colsWide; | NSMatrix, a subclass of NSView |
- 不要使用
and
来链接方法名中的属性关键字
- (int)runModalForDirectory:(NSString *)path file:(NSString *) name types:(NSArray *)fileTypes; | Right. |
---|---|
- (int)runModalForDirectory:(NSString *)path andFile:(NSString *)name andTypes:(NSArray *)fileTypes; | Wrong. |
- 如果方法描述了俩个分离的动作,可以用
and
链接
- (BOOL)openFile:(NSString *)fullPath withApplication:(NSString *)appName andDeactivate:(BOOL)flag; | NSWorkspace. |
---|
####Accessor Methods
- 属性为名词时
- (NSString *)title;
- (void)setTitle:(NSString *)aTitle;
- 属性为形容词时
- (BOOL)isEditable;
- (void)setEditable:(BOOL)flag;
- 属性为动词时
- (BOOL)showsAlpha;
- (void)setShowsAlpha:(BOOL)flag;
- 不要使用过去分词来让动词和形容词组成方法名
- (void)setAcceptsGlyphInfo:(BOOL)flag; | Right. |
---|---|
- (BOOL)acceptsGlyphInfo; | Right. |
- (void)setGlyphInfoAccepted:(BOOL)flag; | Wrong. |
- (BOOL)glyphInfoAccepted; | Wrong. |
- 可以使用情态动词,不要使用
do
/does
- (void)setCanHide:(BOOL)flag; | Right. |
---|---|
- (BOOL)canHide; | Right. |
- (void)setShouldCloseDocument:(BOOL)flag; | Right. |
- (BOOL)shouldCloseDocument; | Right. |
- (void)setDoesAcceptGlyphInfo:(BOOL)flag; | Wrong. |
- (BOOL)doesAcceptGlyphInfo; | Wrong. |
- 间接返回多个值时,可以使用
get
- (void)getLineDash:(float *)pattern count:(int *)count phase:(float *)phase; | NSBezierPath. |
---|
####代理方法
- 以发送消息的对象名作为开始
- (BOOL)tableView:(NSTableView *)tableView shouldSelectRow:(int)row;
- (BOOL)application:(NSApplication *)sender openFile:(NSString *)filename;
类名省略前缀,并且首字母小写
-
一个冒号总是跟在类名后面,如果只有一个参数
sender
像下面的方式命名- (BOOL)applicationOpenUntitledFile:(NSApplication *)sender;
-
一种例外的情况是
sender
发出通知,这是只有一个通知对象作为参数- (void)windowDidChangeScreen:(NSNotification *)notification;
-
可以使用
did
、will
通知代理对象一些事情已经发生或即将发生- (void)browserDidScroll:(NSBrowser *)sender; - (NSUndoManager *)windowWillReturnUndoManager:(NSWindow *)window;
-
也可以使用
did
或will
来向代理对象请求某些行为的发生- (BOOL)windowShouldClose:(id)sender;
####集合方法
集合对象方法模板
- (void)addElement:(elementType)anObj;
- (void)removeElement:(elementType)anObj;
- (NSArray *)elements;
注意点
-
如果集合是无序的,返回
NSSet
要比NSArray
好 -
如果要在集合的指定位置插入或删除元素,用下面的命名方法
- (void)insertLayoutManager:(NSLayoutManager *)obj atIndex:(int)index; - (void)removeLayoutManagerAtIndex:(int)index;
-
集合对象通常拥有被插入的对象,添加或插入对象的时候需要持有对象,删除对象的时候需要释放对象
-
被插入的对象如果想用一个指针指向集合对象,可以用
set
方法来实现但是不要retain
- (void)setTextStorage:(NSTextStorage *)textStorage; - (NSTextStorage *)textStorage;
- (void)addChildWindow:(NSWindow *)childWin ordered:(NSWindowOrderingMode)place;
- (void)removeChildWindow:(NSWindow *)childWin;
- (NSArray *)childWindows;
- (NSWindow *)parentWindow;
- (void)setParentWindow:(NSWindow *)window;
####方法参数
-
参数名首字母小写,接下来的单词首字母大写,
removeObject:(id)anObject
-
不要使用
pointer
、ptr
,让参数的类型来声明 -
避免使用
one-
和two-letter
来做参数名 -
不要使用缩写
Cocoa
中经常一起使用的关键字和参数名
...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
####私有方法
-
为了区分公有方法,可以为私有方法加前缀
-
不要使用下划线作为私有方法的前缀,苹果保留了使用权
-
如果子类化一个大的
Cocoa
框架类, 或者是添加分类方法,可以使用项目或公司简写作为前缀
####命名函数
-
函数名和方法名基本一样,除了下面俩条
- 函数名使用与类、常量相同的前缀
- 前缀后第一个单词首字母大写
-
大部分的函数名以动词开头,描述了函数的作用
NSHighlightRect NSDeallocateObject
-
如果返回值是函数第一个参数的属性,省略动词
unsigned int NSEventMaskFromType(NSEventType type) float NSHeight(NSRect aRect)
-
如果返回值是引用类型,使用
Get
const char *NSGetSizeAndAlignment(const char *typePtr, unsigned int *sizep, unsigned int *alignp)
-
如果返回值是
BOOL
类型,用一个转折变化的动词开头BOOL NSDecimalIsNotANumber(const NSDecimal *decimal)
###属性和数据类型命名规范
####声明属性和实例变量
一个声明的属性实际上也声明了这个属性的存取方法, 宽泛的来说属性的命名规则和访问方法的命名规则相同。如果属性用一个动词或名词来表达,格式如下:
@property (…) type nounOrVerb;
@property (strong) NSString *title;
@property (assign) BOOL showsAlpha;
如果属性用一个形容词来表达,属性名词省略is
, 但是可以在get
方法中添加is
@property (assign, getter=isEditable) BOOL editable;
-
不要直接声明公有实例变量,使用属性代替
-
如果要声明实例变量显示指出
@private
或@protected
-
如果一个实例变量是可访问的,确保实现访问方法
####通知
[类名] + [Did | Will] + [行为] + Notification
NSApplicationDidBecomeActiveNotification
NSWindowDidMiniaturizeNotification
NSTextViewDidChangeSelectionNotification
NSColorPanelColorDidChangeNotification
####异常
[Prefix] + [UniquePartOfName] + Exception
NSColorListIOException
NSColorListNotEditableException
NSDraggingException
NSFontUnavailableException
NSIllegalSelectorException
###框架开发技巧
####初始化
#####类初始化
可以在类方法initialize
中执行一些一次性的代码,这个方法在第一次使用类的时候会调用,通常用来设置类的版本号
如果一个类没有实现initialize
方法,就会调用父类的,所以为了避免一个类的initialize
方法调用多次可以下面的方法来实现该方法
if (self == [NSFoo class]) {
// the initializing code
}
永远不要直接调用initialize
方法,如果需要触发初始化过程,可以调用一些无害的方法例如:
[NSImage self];
#####指定初始化方法
一个指定初始化方法是一个类的init
方法,这个方法会调用父类init
方法。(其它初始化方法会调用指定初始化方法)。每个公有类都应该有一个或多个指定初始化方法。NSView
的initWithFrame:
和NSResponder
的init
方法就是很好的例子。init
并不意味着要重写, 只有像NSString
和其它面向类簇的抽象类,子类需要实现自己的方法。
指定初始化方法应该被清晰的标识,这个信息对子类很重要。一个子类只需要重写指定初始化方法,其它初始化方法将会正常使用。
实现一个框架中的类时,总是会实现这个类的归档方法initWithCoder:
和encodeWithCoder:
,当一个对象没有归档时,不要在这种初始化方法中添加任务代码。可以在指定初始化方法和initWithCoder:
调用一段通用代码。
#####在初始化过程中的错误检测
- (id)init {
self = [super init]; // Call a designated initializer here.
if (self != nil) {
// Initialize object ...
if (someError) {
[self release];
self = nil;
}
}
return self;
}
####异常和错误
大部分的Cocoa
框架方法都不强制开发者捕获及处理异常。因为在一个正常的执行过程中是不会有异常抛出的,对于期望中的运行时和用户错误通常不会使用异常处理。例如
- 文件找不到
- 没有这个用户
- 尝试打开错误类型的文档
- 用指定的编码方式转换字符串发生的错误
但是对于像下面的程序或逻辑错误,Cocoa
会抛出异常:
- 数组越界
- 尝试改变不可变对象
- 错误的参数类型
期望的结果是开发者可以在测试过程中捕获到这些异常错误,在发布程序前处理了这些错误。这样程序就不需要在实际运行中处理异常,如果一个抛出的异常没有被捕获到,顶端的默认处理程序会捕获以及报告异常然后继续。开发者可以替换默认的异常捕获器,可以提供更加详细的错误信息以及保持数据和退出程序的选项。
错误是Cocoa
框架不同于别的软件库的另一个地方。Cocoa
方法通常不会反悔错误码。当一个预期的或者可能的错误发生时, Cocoa
方法可以返回bool
值或者nil
。你不应该使用错误码来提示需要在运行时处理的程序错误,要抛出异常或者简单用日志记录。
例如,NSDictionary’s objectForKey:
方法要么返回找到的对象,要么返回nil. NSArray’s objectAtIndex:
永远不能返回nil
,因为NSArray
对象不会存储nil
值,对于越界的参数需要抛出异常。大部分的初始化方法应该返回nil
在初始化失败的时候。
在少数情况下一个方法确实需要多个不同的错误码, 应该用解引用的方式返回错误信息。例如使用NSError
对象,这个方法需要注意**NSError
参数的可选情况,对于不关心错误信息的发送者会传nil
。