注:本文章只翻译前一部分内容,即:Cocoa命名规范
=========================================章节分割线=========================================
代码越简洁越明确越好,但是不能因为简洁而导致语义不明确:
代码 | 评价 |
insertObject: atIndex: | 好 |
insert:at: | 不明确,什么被插入?at指什么 |
removeObjectAtIndex: | 好 |
removeObject: | 好,没有之前讨论的那些问题 |
remove: | 不明确,什么被移除了 |
代码 | 评价 |
destinationSelection | 好 |
destSel | 不明确 |
setBackgroundColor: | 好 |
setBkgdColor: | 不明确 |
--当然,一些公认的通用的缩写,还是可以用。参考“通用的缩写”章节
在API的命名上避免歧义,例如方法的名字可以有多个解释:
code | 评价 |
sendPort | 是发送端口还是返回端口 |
displayName | 它是显示一个名字还是在用户界面返回消息发送者的标题 |
始终使用Cocoa 编程接口的名字。如果你不太确定,查看已有的头文件和参考文档。
一致性非常重要,当你有个实现多态方法的类。不同类中处理同一个任务的方法应该拥有同样的名称。
code | 评价 |
- (NSInteger)tag | 在 NSView, NSCell, NSControl类中都要定义 |
code | 评价 |
NSString | okay |
NSStringObject | 自我引用 |
code | 评价 |
NSUnderlineByWordMask | okay |
NSTableViewColumnDidMoveNotification | okay |
--对于函数和常数,和相关类使用相同的前缀,并且大写第一个字母。例:NSRunAlertPanel、NSCellDisabled。 --避免使用下划线作为前缀意义在于会导致方法名称私有的意思(可以用它做实例变量)。Apple保留使用该规则。但在第三方用可能导致命名冲突,他们会不自觉的重写自己已有的一个私有方法。参考“私有方法”章节。
协议的命名应该根据使用协议的相应类行为命名。 --大多数协议包含的相关方法,不与任何特定的类关联。这种协议的应该命名为使协议与类不能混淆。一个通常的规则是用动名词(...ing)。对比NSLocking 、NSLock(看起来像类名)。 --有的协议包含一些没什么联系的方法(而不是创建多个独立的小协议)。这些协议跟一个类的联系很大,这个类主要体现了这个协议。这种情况下,命名规则为协议名跟类名字一样。一个例子是NSObject 协议,这个协议包含一些方法可以查询任何类在父类中的层次位置等。因为NSObject类实现了协议的大部分方法,所以协议可以以类名命名。
--声明联系的类/协议:如果有一些联系的声明(类、协议、分类),将它们声明放到一个文件中,文件的命名根据基础的类、协议、分类;
头文件 | 声明 |
NSString.h | NSString和NSSMutableString |
NSLock.h | NSLocking协议、NSLock、NSConditionLock、NSRecursive类 |
不要使用do或does这样的词做名字一部分,因为这些辅助动词没什么意义,同时不要在动词前使用副词或形容词。-----如果方法返回的是消息发送者(对象)的属性,用属性命名方法。get这个词不需要,除非有多个间接返回的值。可以参考“存取器方法”小节。
- (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; | UIView |
- (id)initWithFrame.:(NSRect)frameRect mode:(int)aMode cellClass:(Class)factoryId numberOfRows:(int)rowsHigh numberOfColumns:(int)colsWide; | NSMatrix, a subclass of
NSView |
- (int)runModalForDirectory:(NSString *)path file:(NSString *) name types:(NSArray *)fileTypes; | right |
- (int)runModalForDirectory:(NSString *)path andFile:(NSString *)name andTypes:(NSArray *)fileTypes; | wrong |
例:- (BOOL)showsAlpha; - (void)setShowsAlpha:(BOOL)flag; 动词是现在时;----在属性的名称中,不要通过用分词形式将动词转换为形容词;
- (void)setAcceptsGlyphInfo:(BOOL)flag; | right |
- (BOOL)acceptsGlyphInfo; | right |
- (void)setGlyphInfoAccepted:(BOOL)flag; | wrong |
- (BOOL)glyphInfoAccepted; | wrong |
- (void)setCanHide:(BOOL)flag; | Y |
- (BOOL)canHide; | Y |
- (void)setShouldCloseDocument:(BOOL)flag; | Y |
- (BOOL)shouldCloseDocument; | Y |
- (void)setDoesAcceptGlyphInfo:(BOOL)flag; | N |
- (BOOL)doesAcceptGlyphInfo; | N |
- (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; 以下一些实现细节要注意:----这些方法通常暗含插入对象的拥有权(ownership)的管理,所以添加/插入元素的时候retain它们,移除的时候remove它们;----如果插入对象想保持它原来的持有的对象,通常对该对象的setter方法不用retain,例如insertLayoutManager:atIndex: method方法。NSLayoutManager类在以下的方法中同样这样处理:- (void)setTextStorage:(NSTextStorage *)textStorage; - (NSTextStorage *)textStorage; 通常你不用调用setTextStorage方法,但是你可能需要重写它。 ----(这段难理解,上面属于个人见解,参考原文:If the inserted objects need to have a pointer back to the main object, you do this (typically) with a set...method that sets the back pointer but does not retain. In the case of the insertLayoutManager:atIndex: method, the NSLayoutManager class does this in these methods: - (void)setTextStorage:(NSTextStorage *)textStorage; - (NSTextStorage *)textStorage; You would normally not call setTextStorage: directly, but might want to override it.) 以上说的集合方法的规则在NSWindow类中都有例子:
- (void)addChildWindow:(NSWindow *)childWin ordered:(NSWindowOrderingMode)place; |
- (void)removeChildWindow:(NSWindow *)childWin; |
- (NSArray *)childWindows; |
- (NSWindow *)parentWindow; |
- (void)setParentWindow:(NSWindow *)window; |
...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框架类的子类,你不知道你的私有方法是否无意中重写方法里同名称的私有方法。 在Cocoa框架中命名大多数私有的方法用下划线前缀开头(例如,_foodata),标记方法为私有。对于这条规则,有两个建议:-----对于你自己的私有方法,不要使用下划线前缀。Apple约定了这条规则;-----如果是一个大cocoa框架类(如NSView)的子类,你要绝对确保你的私有的方法不同于父类的方法,您可以通过添加你自己独有的前缀来区分。前缀应尽可能的唯一的,也许是一个基于在你公司或项目的形式”xx_”。所以如果你的项目被称为Byte Flogger,前缀可以是BF_addobject;虽然之前建议用前缀给私有方法命名,这看起来跟之前说的规则矛盾。但这块情况特殊,我们必须确保子类无意间重写父类的私有方法。 =========================================章节分割线=========================================
@implementation MyClass {BOOL _showsTitle;}
如果你想用某实例变量对应某个属性,在@synthesize中说明:@implementation MyClass @synthesize showsTitle=_showsTitle; 当添加实例变量的时候,有以下几条规则:
-----避免显示的声明公共的实例变量。开发者只会关心对象的接口,不关心实现的细节。通过声明属性和相应的(synthesizing)实例变量,避免显示声明实例变量。-----如果需要声明实例变量,用@private 或者 @protected声明。如要继承的实例变量用@protected声明。-----如果一个实例变量是实例的访问属性(accessible attribute),确保你已经写了相应的存取器方法
。
----对于有取值相联系的常数集合,使用枚举(说什么情况使用枚举,跟命名没关系)
----枚举常数和typedef后面枚举名的命名跟函数的命名规则类似,参考函数命名小节。例:
typedef enum _NSMatrixMode {NSRadioModeMatrix = 0,NSHighlightModeMatrix = 1,NSListModeMatrix = 2,NSTrackModeMatrix = 3} NSMatrixMode;
注意上文中_NSMatrixMode 在typedef中没有用。
----你也可以使用不命名的枚举,比如位掩码(bit masks),例如:
enum {NSBorderlessWindowMask = 0,NSTitledWindowMask = 1 << 0,NSClosableWindowMask = 1 << 1,NSMiniaturizableWindowMask = 1 << 2,NSResizableWindowMask = 1 << 3};
-----使用const去创建浮点型常量。可以创建整形常量,如果各整形常量之间没有什么联系,否则,使用枚举。
----const修饰的常数命名规则,举例说明:const float NSLightGray; 命名规则类似函数,参考函数命名小节。
----通常不使用#define预编译命令去创建常数。像上文说的,整形常数用枚举,浮点型常数用const修饰。
----使用大写字母符号让编译器决定某段代码是否编译。例如:#ifdef DEBUG
----注意由编译器定义的宏,有前后各俩个下划线。例如:__MACH__;
----定义字符串常数,例如作方法名或字典的key等,你要确保编译器识别字符串常数(编译语法检查)。Cocoa提供了许多字符串常量例子,如:APPKIT_EXTERN NSString *NSPrintCopies; 字符串的值被指定了常量(注意APPKIT_EXTERN 宏在Objective-C中的像extern声明的作用)
如果一个类有delegate,许多通知都会被delegate接收通过delegate方法。这些通知的名称应该反应相应的delegate方法。例如,一个全局的NSApplication类对象自动注册去接收applicationDidBecomeActive消息,当应用程序发送NSApplicationDidBecomeActiveNotification.消息的时候。通知通过全局的字符串对象定义,格式如下:[Name of associated class] + [Did | Will] + [UniquePartOfName] + Notification;例:
NSApplicationDidBecomeActiveNotificationNSWindowDidMiniaturizeNotificationNSTextViewDidChangeSelectionNotificationNSColorPanelColorDidChangeNotification
尽管你可以为了一些目的自由的使用异常(由NSException类和相关函数提供),Cocoa将编程中出现错误,例如数组越界,看做异常。Cocoa不使用异常去处理常规的、预料的错误情况,例如,返回值为nil、NULL、NO或一些错误代码。详细参考《Error Handling Programming Guide》。
异常通过全局的字符串对象定义,格式如下:[Prefix] + [UniquePartOfName] + Exception;其中unique part of the name是由单词组合而成,每个首字母大写。例如:
NSColorListIOExceptionNSColorListNotEditableExceptionNSDraggingExceptionNSFontUnavailableExceptionNSIllegalSelectorException
=========================================章节分割线=========================================
通常你不用缩写你的命名,当你编写接口时候。参考基本命名规则章节。然而,下面所列举的缩写都是众所周知的,你可以继续使用它们。有以下几点需要注意:
----缩写的替代格式使用在标准C语音库中被允许。例如:“alloc” and “getc”。
----你可以在参数中更自由的使用缩写。例如:“imageRep”, “col” (for “column”),“obj”, and “otherWin”缩写 | 意义 |
alloc | Allocate |
alt | Alternate |
app | 应用程序,例, NSApp全局应用程序对象。 “application” 全拼在delegate方法、通知中等 |
calc | Calculate. |
dealloc | Deallocate. |
func | Function. |
horiz | Horizontal. |
info | Information |
init | Initialize |
max/min | Maximum/Minimum. |
msg | Message |
nib | Interface Builder archive. |
pboard | Pasteboard (but only in constants |
rect | Rectangle. |
Rep | Representation (used in class name such as NSBitmapImageRep |
temp | Temporary. |
vert | Vertical. |