iOS学习开发iOS DeveloperiOS 开发

Cocoa编码指南

2016-08-12  本文已影响430人  lakerszhy

翻译自“Coding Guidelines for Cocoa”

0 简介

使用公开的API开发一个Cocoa框架,插件,或者其它可执行文件需要的方法和规范,跟开发应用程序的不一样。产品的主要用户是开发者,他们不会被编程接口迷惑。此时API的命名规范就派上了用场,它们让接口保持清晰和一致性。同时它们也是编程技术,尤其对框架,例如版本控制,二进制兼容性,错误处理和内存管理。这个专题同时包括Cocoa命名规范和推荐的框架编程实践。

0.1 文档结构

本专题的文章分为两类。第一类是编程接口的命名规范。这些规范与Apple自己的Cocoa框架一样(有一些小的区别)。下面是命名规范的文章:
代码命名基础
方法命名
函数命名
属性和数据类型命名
可接受的缩写和首字母缩写

第二部分讨论框架编程相关的规范。
框架开发者的技巧和技术

1 代码命名基础

设计面向对象软件库时,类,协议,方法,函数,常量,以及其它元素的命名经常被忽略。这一节讨论大部分Cocoa接口的命名规范。

1.1 一般性原则

清晰

代码 点评
insertObject:atIndex:
insert:at: 不清晰;插入什么?“at”表示什么?
removeObjectAtIndex:
removeObject: 好,因为参数指定了要移除的对象
remove 不清晰;要移除什么?
代码 点评
destinationSelection
destSel 不清晰
setBackgroundColor:
setBkgdColor: 不清晰

你可能会认为某个缩写广为人知,但可能并非如此,尤其是当方法或函数名被不同文化和语言背景的开发人员使用时。

代码 点评
sendPort 发送端口还是返回一个发送端口?
displayName 显示名称还是返回用户界面中接收者的标题?

一致性

代码 点评
- (NSInteger)tag NSView, NSCell, NSControl中有定义
*- (void)setStringValue:(NSString ) 在许多Cocoa类中有定义

参考“方法参数”一节。

不要自我参考

代码 点评
NSString 可以
NSStringObject 自我参考
代码 点评
NSUnderlineByWordMask 可以
NSTableViewColumnDidMoveNotification 可以

1.2 前缀

前缀是名称的重要组成部分。它们可以区分软件的功能范围。通常,软件被打包成一个框架,或者多个紧密关联的框架(如FoundationApplication Kit框架)。前缀可以防止第三方开发者与Apple之间符号的命名冲突(也可以防止Apple内部不同框架之间的冲突)。

前缀 Cocoa框架
NS Foundation
NS Application Kit
AB Address Book
IB Interface Builder

1.3 书写规范

为API元素命名时,遵循以下简单的书写规范:

fileExistsAtPath:isDirectory:

该规则的一个例外是方法名以一个广为人知的缩写开头,例如:TIFFRepresentation (NSImage)

NSRunAlertPanel
NSCellDisabled

1.4 类和协议的名字

类名应该包含一个明确描述类(或类的对象)是什么或者做什么的名词。类名要有一个合适的前缀(参考“前缀”一节)。FoundationApplication Kit框架很多这样的例子,例如NSStringNSDateNSScannerNSApplicationUIApplicationNSButtonUIButton

协议应该根据如何分组行为来命名:

代码 点评
NSLocking
NSLock 不好(像类名)

NSObject协议就是这样一个例子。该协议组合不相关的方法,包括查询对象在类继承关系中位置的方法,调用特殊方法的方法,增加或减少引用计数的方法。因为NSObject类是这些方法的主要体现者,所以用类名命名这个协议。

1.5 头文件

如何命名头文件很重要,因为使用的命名规范表明了头文件的内容。

头文件 声明
NSLocale.h NSLocale
头文件 声明
NSString.h NSStringNSMutableString
NSLock.h NSLocking协议和NSLockNSConditionLockNSRecursiveLock

2 方法命名

方法可能是编程接口中最常见的元素,因此要非常小心的为它们命名。本节讨论方法命名的以下几个方法:

2.1 一般性规则

为方法命名时,记住以下一般性指南:

- (void)invokeWithTarget:(id)target;
- (void)selectTabViewItem:(NSTabViewItem *)tabViewItem

不要使用“do”或“does”作为名称的一部分,因为这些助动词没有实际意义。同样的,动词之前不要使用副词或形容词。

方法名 点评
- (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; 错误
方法名 点评
- (id)initWithFrame:(CGRect)frameRect; NSView,UIView
- (id)initWithFrame:(NSRect)frameRect mode:(int)aMode cellClass:(Class)factoryId numberOfRows:(int)rowsHigh numberOfColumns:(int)colsWide; NSMatrix,NSView的子类
方法名 点评
- (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; NSWorkspace

2.2 访问方法

访问方法是对象属性的读取和设置方法,其命名的特定推荐格式依赖于如何描述属性:

- (type)noun;
- (void)setNoun:(type)aNoun;

例如:

- (NSString *)title;
- (void)setTitle:(NSString *)aTitle;
- (BOOL)isAdjective;
- (void)setAdjective:(BOOL)flag;

例如:

- (BOOL)isEditable;
- (void)setEditable:(BOOL)flag;
- (BOOL)verbObject;
- (void)setVerbObject:(BOOL)flag;

例如:

- (BOOL)showsAlpha;
- (void)setShowsAlpha:(BOOL)flag;
方法名 点评
- (void)setAcceptsGlyphInfo:(BOOL)flag; 正确
- (BOOL)acceptsGlyphInfo; 正确
- (void)setGlyphInfoAccepted:(BOOL)flag; 错误
- (BOOL)glyphInfoAccepted; 错误
方法名 点评
- (void)setCanHide:(BOOL)flag; 正确
- (BOOL)canHide; 正确
- (void)setShouldCloseDocument:(BOOL)flag; 正确
- (BOOL)shouldCloseDocument; 正确
- (void)setDoesAcceptGlyphInfo:(BOOL)flag; 错误
- (BOOL)doesAcceptGlyphInfo; 错误
方法名 点评
*- (void)getLineDash:(float *)pattern count:(int *)count phase:(float )phase; NSBezierPath

像上面这样的方法,实现时应该允许接收NULL作为in/out参数,表示调用者不需要一个或多个返回值。

2.3 代理方法

特定事件发生时,对象在其代理中(如果代理实现了代理方法)调用代理方法。它们有独特的格式,同样也适用于对象的数据源方法。

- (BOOL)tableView:(NSTableView *)tableView shouldSelectRow:(int)row;
- (BOOL)application:(NSApplication *)sender openFile:(NSString *)filename;
- (BOOL)applicationOpenUntitledFile:(NSApplication *)sender;
- (void)windowDidChangeScreen:(NSNotification *)notification;
- (void)browserDidScroll:(NSBrowser *)sender;
- (NSUndoManager *)windowWillReturnUndoManager:(NSWindow *)window;
- (BOOL)windowShouldClose:(id)sender;

2.4 集合方法

管理对象集合的对象(集合中的对象叫做元素)方法具备如下的格式:

- (void)addElement:(elementType)anObj;
- (void)removeElement:(elementType)anObj;
- (NSArray *)elements;

例如:

- (void)addLayoutManager:(NSLayoutManager *)obj;
- (void)removeLayoutManager:(NSLayoutManager *)obj;
- (NSArray *)layoutManagers;

下面是集合方法命名的一些限制和规定:

- (void)insertLayoutManager:(NSLayoutManager *)obj atIndex:(int)index;
- (void)removeLayoutManagerAtIndex:(int)index;

集合方法的实现需要考虑以下细节:

- (void)setTextStorage:(NSTextStorage *)textStorage;
- (NSTextStorage *)textStorage;

通常不会直接调用setTextStorage:方法,而是覆写它。

另一个关于上面集合方法规范的例子是NSWindow类:

- (void)addChildWindow:(NSWindow *)childWin ordered:(NSWindowOrderingMode)place;
- (void)removeChildWindow:(NSWindow *)childWin;
- (NSArray *)childWindows;
 
- (NSWindow *)parentWindow;
- (void)setParentWindow:(NSWindow *)window;

2.5 方法参数

命名方法参数需要考虑以下一般性规则:

按惯例(在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

2.6 私有方法

大多数情况下,私有方法的命名与公共方法命名的规范相同。但通常给私有方法加一个前缀,以便与公共方法区分开来。即使在这个规范下,私有方法名还是会导致奇怪的问题。设计一个从Cocoa框架继承的子类时,不知道自己的私有方法是否无意的覆盖了框架中同名的私有方法。

Cocoa框架中大部分私有方法名使用下划线(例如:_fooData)标志为私有的。因此遵循下面两条建议:

尽管为私有方法添加前缀的建议与之前为方法命名的规范自相矛盾,但这里的目的不一样:防止无意覆写父类中的私有方法。

3 函数命名

Objective-C允许使用函数描述行为,如同方法一样。当对象是单例时,或者处理明显的函数式子系统时,应该使用函数而不是类方法。

函数命名应该遵循如下一般性的规则:

NSHighlightRect
NSDeallocateObject

查询属性的函数有更多的命名规则:

unsigned int NSEventMaskFromType(NSEventType type)
float NSHeight(NSRect aRect)
const char *NSGetSizeAndAlignment(const char *typePtr, unsigned int *sizep, unsigned int *alignp)
BOOL NSDecimalIsNotANumber(const NSDecimal *decimal)

4 属性和数据类型命名

本节描述声明属性,实例变量,常量,通知和异常的命名规范。

4.1 声明属性和实例变量

一个声明的属性实际上声明了属性的一个访问方法,因此属性的命名规范大体上与访问方法相同(参考“访问方法”)。如果属性用名称或动词描述,格式为:

@property (…) type nounOrVerb;

例如:

@property (strong) NSString *title;
@property (assign) BOOL showsAlpha;

如果属性用形容词描述,省略“is”前缀,但指明get访问器的规范的名称,例如:

@property (assign, getter=isEditable) BOOL editable;

多数情况下,使用一个声明的属性时,同时生成了一个相应的实例变量。

确保实例变量简明扼要的描述了存储的属性。通常不直接访问实例变量,而是使用访问方法(在init和dealloc方法中直接访问实例变量)。为了达到这个目的,在实例变量名之前添加下划线作为前置,例如:

@implementation MyClass {
    BOOL _showsTitle;
}

如果使用一个生命的属性生成实例变量,在@synthesize中指定实例变量的名称。

@implementation MyClass
@synthesize showsTitle=_showsTitle;

为类添加实例变量时需要考虑以下几个方面:

4.2 常量

常量命名规则根据创建方式的不同而大不相同。

4.2.1 枚举常量

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
 
};

4.2.2 使用const创建的常量

const float NSLightGray;

枚举常量的命名规范跟函数命名规范相同。

4.2.3 其它类型的常量

#ifdef DEBUG
__MACH__
APPKIT_EXTERN NSString *NSPrintCopies;

字符串在实现文件中赋值。(注意:APPKIT_EXTERN宏在Objective-C中等价于extern)

4.3 通知和异常

通知和异常的命名遵循相似的规则,但它们有各自的推荐使用模式。

4.3.1 通知

如果一个类有代理,那它的大部分通知可能由代理类的代理方法接收。这些通知的名称应该能够反应对应的代理方法。例如,当应用程序提交NSApplicationDidBecomeActiveNotification通知时,全局的NSApplication对象的代理自动注册接收applicationDidBecomeActive:消息。

通知由如下形式的全局NSString对象标识:

[Name of associated class] + [Did | Will] + [UniquePartOfName] + Notification

例如:

NSApplicationDidBecomeActiveNotification
NSWindowDidMiniaturizeNotification
NSTextViewDidChangeSelectionNotification
NSColorPanelColorDidChangeNotification

4.3.2 异常

虽然可以因为任何目的而使用异常(由NSException类和相关函数实现),但Cocoa使用异常来处理类似数组越界的编程错误。Cocoa不适用异常处理常规的,可预料的错误。这些情况下,使用nil,NULL,NO,或错误代码之类的返回值。

异常由如下形式的全局NSString对象标识:

[Prefix] + [UniquePartOfName] + Exception

名称的唯一部分(UniquePartOfName)部分由单词组成,每个单词的第一个字母大写。例如:

NSColorListIOException
NSColorListNotEditableException
NSDraggingException
NSFontUnavailableException
NSIllegalSelectorException

5 可接受的缩写和首字母缩写

设计编程接口时,通常不使用缩写(参考“一般性规则”)。然后下面列出的缩写要么是相沿成习,要么是过去广泛使用的,所以可以继续使用。关于缩写有一些额外的注意事项:

缩写 含义和注释
alloc Allocate
app Application。例如,全局application对象NSApp。但在代理方法,通知等中,应该使用“application”全拼。
alt Alternate
calc Calculate
dealloc Deallocate
func Function
horiz Horizontal
info Information
init Initialize
int Integer
max Maximum
min Minimum
msg Message
nib Interface Builder文件
pboard Pasteboard(只在常量中)
rect Rectangle
Rep Representation(在类名中使用,如NSBitmapImageRep)
temp Temporary
vert Vertical

可以使用计算机行业的常见缩写和首字母缩写,例如:

ASCII
PDF
XML
HTML
URL
RTF
HTTP
TIFF
JPG
PNG
GIF
LZW
ROM
RGB
CMYK
MIDI
FTP

6 框架开发者的小贴士和技术

没有开发框架,不敢随便翻译,以后有机会再翻译。

上一篇下一篇

猜你喜欢

热点阅读