iOS-CoCoa编码规范

对于开发人员来说,代码规范很重要!

介绍 Cocoa 编码指南

使用公共 API 开发 Cocoa 框架、插件及其他可执行文件需要使用的方法和约定不同于应用程序开发。如果产品主要客户是开发人员,则保证产品的编程接口清晰明确,不至于让开发者产生疑惑十分重要。这种情况下,API 命名约定就可以派上用场,它可以帮助您保持编程接口一致明确。另外,框架开发领域也存在一些特定的编程技术或者说,这些技术对框架开发更加重要例如版本管理,二进制兼容性,错误处理以及内存管理等。本节主题包括 Cocoa 命名约定以及我们提倡的框架编程实践。

本文档的组织方式

本主题的文章大致分成两类。第一类数量较多,讲述编程接口的命名约定。苹果公司的 Cocoa框架使用了这里介绍的命名约定(只有一些小的例外)。该类文章包括如下:

  •   代码命名基础

  •   为方法命名

  •   为函数命名

  •   为实例变量和数据类型命名

  •   可以使用的缩略名称

    第二类的文章(目前只有一篇)讨论了框架编程方面的内容。

框架开发者可以使用的技巧和技术

代码命名基础在面向对象软件库的设计过程中,开发人员经常忽视对类、方法、函数、常量以及其他编程接元

素的命名。本节讨论大多数 Cocoa 接口通用的几条命名约定。

一般性原则

清晰
最好是既清晰又尽可能地简短,但不要为了追求简短而丧失清晰性:

page1image6392 page1image6552 page1image6712 page1image6872 page1image7032 page1image7192 page1image7352 page1image7512
page1image8112page1image8864
insertObject:atIndex:

好的命名

page2image992page2image1584

insert:at:

page2image2832page2image3424page2image3584page2image4176
page2image4488page2image4912

不清晰;插入什么?“at”表示什么?

page2image6208page2image6632
removeObjectAtIndex:

好的命名

page2image9832page2image10424page2image10744page2image11336
removeObject:
page2image12584page2image13176page2image13336page2image13928
page2image14248page2image14672
这样命名也不错,因为方法将移除通过参数引用的对象。
page2image16128page2image16552
page2image17112

remove:

page2image19152
page2image19784

不清晰:要移除什么?

page2image20656

通常情况下,请不要缩写事物的名称,即使名称很长,也应该把它完全拼写出来。

destinationSelection
page2image23968page2image24392page2image24552

好的命名

destSel

page2image29432page2image29592page2image30184page2image30344

不清晰

page2image32288page2image32448page2image33040page2image33200
setBackgroundColor:

好的命名

setBkgdColor:
page2image39280page2image39440page2image40032page2image40192page2image41424

不清晰

page2image42296page2image42456

您可能觉得某个缩写众所周知,但实际可能并非如此。特别是具有不同文化和语言背景的开发人员,在遇到您提供的方法或函数的名称缩写时,他们可能不明白其中的含义。

不过,有一些缩写确实很常见并且有很长的使用历史。因此,您可以继续使用。请参看可以接受的缩略名称一节以了解更多的信息。

要防止 API 的名称出现歧义。这里的歧义是指名称具有多种解释方式。

一致性

page2image44912
page2image45704page2image46296page2image46456

sendPort

page2image48160page2image48320page2image48912
page2image49224page2image49648
该方法是把端口发送出去还是返回发送端口呢?
page2image50944page2image51368
page2image51928

displayName

page2image53968
page2image54600
该方法是显示某个名称还是返回用户界面中接收者的标题呢?
page2image55472

请尽可能在 Cocoa 编程接口中保持名称一致性。如果不太有把握做到这一点,则请浏览一下头文件和参考文档中的范例。

如果类方法利用多态,一致性就显得尤其重要。因为在这种情况下,不同类用于完成同样事件的方法必须具有相同的名称。

- (int)tag

page2image59800page2image60224page2image60384

该方法同时定义在 NSViewNSCellNSControl 这三个类里面。

page2image62584page2image63168
- (void)setStringValue:(NSString
page2image64672

该方法定义于数个 Cocoa 类中

*)

page3image2944page3image3368page3image3528page3image4600page3image5024page3image5184

您可以参看 方法参数一节。不能自我指涉

名称不应该自我指涉。

掩码的常量(可以使用位操作进行组合)不适用这条规则,作为通告的常量也不适用。

前缀

前缀是编程接口名称的重要部分,它们可以区分软件的功能范畴。通常情况下,提供编程接口的软件会被打包成框架(Foundation 框架以及 Application Kit 框架就是如此)或者是和框架紧密相关的产品,我们可以利用前缀来区分框架的功能范畴。另外,前缀可以防止第三方开发者定义的符号和苹果公司定义的符号发生冲突(以及防止苹果公司不同框架之间的符号发生冲突)。

前缀有规定的格式。它需要由两个或者三个大写字符组成,而且不能使用下划线或者子前缀。下面是一些例子:

page3image8864
page3image9488page3image10080

NSString

page3image11464page3image11888

可以使用

page3image13360page3image13952page3image14272page3image14864page3image15024

NSStringObject

page3image16896
page3image17224page3image17648page3image18392

该名称自我指涉

page3image19264
page3image20280
NSUnderlineByWordMask
page3image22760page3image23352
NSTableViewColumnDidMoveNotification
page3image26240
page3image27112page3image27704

前缀

page3image29112page3image29704page3image29864page3image30456
page3image30776page3image31200

Cocoa 的框架

page3image32696page3image33120

NS

Foundation 框架

page3image36328page3image36920page3image37080page3image37672

NS

page3image39224page3image39648

Application Kit 框架

page3image41400page3image41992page3image42312page3image42904

AB

page3image44152page3image44744page3image44904page3image45496
page3image45816page3image46240

Address Book 框架

page3image47776page3image48200
page3image48760

IB

page3image50800
page3image51248

Interface Builder 框架

在为类,协议,函数,常量以及通过 typedef 定义的结构命名时,请使用前缀。但在命名方法时,请不要使用前缀,因为方法已经存在于其定义类所创建的名称空间中。同理,在定义结构的字段时,也不要使用前缀。

书写约定

API 元素命名的时候, 请遵循下面这几条简单的书写约定:

对于含有多个单词的名称,请不要使用标点符号标志和分割符(下划线,破折号之类);而是要大写每个单词的首字符并且把这些单词连续地拼写在一起。然而如下这些限定条件您也需要注意:

方法的名称要以一个小写字符开头,而名称中单词的首字符应该大写。另外,请不要在方法的名称中使用前缀。

如果方法名称的开头是某个众所周知的缩略语,则该原则就不适用。例如TIFFRepresentation (NSImage),该名称就不遵循该原则。

函数或常量名称使用其关联类的前缀,并且要名称中单词的首字符要大写。

请不要使用下划线作为前缀来表示私有的属性,尤其是不要在类方法中使用。因为苹果公司保留使用这种方式,如果第三方再使用,就有可能会导致名称空间冲突。他们有可能在无意中用自己的方法覆盖了一个已经存在的私有方法,这样做将会带来灾难性的后果。请参看私有方法一节。您可以了解到我们提倡的可供私有API使用的约定。

类和协议的名称

类的名称应包含一个名词,这个名词明确地指示这个类(或者类对象)表示什么或者要做什么。此外,类名称还应该包含适当的前缀。(请参考前缀一节)。Foundation框架以及Application Kit框架就有很多这样的例子,例如NSString, NSDate,NSScanner,NSApplication,NSButton,以及NSEvent

我们应根据协议对方法的分组方式来为其命名:

大部分协议会把一些彼此相关但又不合类关联的方法归结在一起,形成一个特殊的方法集合。这种协议要合理地命名,不要将其和类名混淆。一种常见的约定是使用动名词格式(“...ing”):

page4image5680 page4image5840 page4image6264 page4image6688 page4image8456
fileExistsAtPath:isDirectory:
page4image10040page4image10464
NSRunAlertPanel
page4image12296page4image13208page4image14976
NSCellDisabled
page4image15848 page4image16008 page4image16168
page4image16984page4image17576

NSLocking

page4image18824page4image19416page4image19576page4image20168
page4image20480page4image20904

page4image22200page4image22624
page4image23184

NSLock

差(看起来像是个类名)

有一些协议会把一些彼此无关的方法归结在一起(不是创建几个独立的小协议)。对于这样的协议,我们倾向于把它和一个类联系起来,利用类来作为协议的主要表现。并且,我们约定让此种协议使用和类一样的名称。

NSObject 协议就是这种情况。它把一些不相关的方法组合在一起,这些方法有的用于查询任何对象在类层次中的位置,有的可以调用对象的特定方法,有的可以用来增加或者减少对象的引用计数。由于 NSObject 类提供了这些方法的主要表现,所以我们使用类名作为协议名称。

头文件

头文件的命名方式很重要,因为通过使用合理的命名约定,您利用文件名称来指示文件中包含的内容:

page5image3192

声明一个独立的类或协议。.如果一个类或协议不属于某个群,则请将其声明放置在一份独立的文件,并使用其名称作为文件名。

声明相关联的类或者协议:如果一群声明(类,类别以及协议)彼此相关,则请将它们放在一份文件,并使用主要的类或者协议名称作为文件名。

头文件

page5image8072page5image8496page5image8656

声明

page5image10600page5image10760page5image11352page5image11512
NSApplication.h
page5image14680

NSApplication

page5image15592page5image15752
page5image17184page5image17776

头文件

page5image19184page5image19776page5image19936page5image20528
page5image20864page5image21448page5image21768

声明

page5image23128

NSString.h

page5image24720page5image25312page5image25472page5image26064
page5image26536

NSString NSMutableString 这两个类

page5image28016
page5image28312

NSLock.h

page5image30352
page5image30992

NSLocking 协议以及 NSLock, NSConditionLock,和NSRecursiveLock 这几个类

page5image32328

包含框架头文件。每个框架都应该包含一份头文件,它的名称和框架名相同,而内容则包含了框架的全部公共头文件。

为另一个框架里的某个类添加 API。如果您在一个框架中声明一些方法,而这些方法属于另一个框架中某个类的范畴类,则请在原始类的名称后加上“Additions”,然后将其作为头文件的名称。例如 Application Kit 框架中的 NSBundleAdditions.h 头文件就是这种处理方式。

page5image35440page5image36032

头文件

page5image37440page5image38032page5image38192page5image38784
page5image39104page5image39528

框架

page5image40984page5image41408
page5image41968
Foundation.h
page5image44008
page5image44640

Foundation 框架

page5image45552

相关联的函数和数据类型。如果一群函数,常量,结构以及其他数据类型彼此相互关联,则请将它们放入到合理命名的头文件,例如 NSGaphics.h(位于 Application Kit)

为方法命名

方法可能是编程接口中最常见的元素了,因此对其命名要特别注意。本部分讨论方法命名的相关方面:

通用规则

为方法命名时,请记住下面这些通用的指导原则:
page6image2376



方法名称应以小写字符开头,名称中的单词首字符要大写。另外,请不要在方法名称中使用前缀。您可以参考书写约定一节,以了解更多信息。有两种特定的情况不适用该原则。其一,方法的名称可以使用某个众所周知的缩写开头,而该缩写可以大写(例如,TIFF 或者PDF)。其二,您可以使用前缀来分组并区分私有方法(请参考私有方法一节)。

 如果方法代表一个对象执行的动作,则其名称应该以一个动词开头:

请不要使用 “do”或者 “does”作为名称的一部分,因为这些辅助性的动词 不能为名称增加更多的含义。同时,请不要在动词之前使用副词或者形容词。

如果方法返回接收者的某个属性,则以属性名称作为方法名。如果方法没有间接地返回一个或多个值,您也无须使用”get“这样的单词。

您可以参考 存取方法一节,以了解更多的信息。所有参数前面都应使用关键字。

page6image6224 page6image6384
page6image7176page6image7920
- (void)invokeWithTarget:(id)target;
page6image9768page6image10568page6image11208
- (void)selectTabViewItem:(NSTabViewItem *)tabViewItem
page6image12480
- (NSSize)cellSize;
page6image14904page6image15328page6image15488

正确

- (NSSize)calcCellSize;
page6image20408page6image20568page6image21160page6image21320

错误

page6image23264page6image23424page6image24016page6image24176
- (NSSize)getCellSize;
page6image27032

错误

page6image28912



page6image29664
- (void)sendAction:(SEL)aSelector to:(id)anObject
forAllCells:(BOOL)flag;
page6image32008
page6image32520page6image32944page6image33104

正确

- (void)sendAction:(SEL)aSelector :(id)anObject :(BOOL)flag;
page7image2888page7image3312page7image3472page7image4544

错误

page7image5560page7image5720

参数前面的单词应能够对参数进行描述。

如果您当前创建的方法比起它所继承的方法更有针对性,则您应该在已有的方法名称后面添加关键字,并将其作为新方法的名称。

请不要使用”and“来连接两个表示接受者属性的关键字。

虽然上面的例子使用”and“这个词感觉还不错,但是随着创建的方法所带有的关键字越来越多,这种方式会引起问题。

如果方法描述了两个独立的动作,请使用”and“把它们连接起来。

存取方法

存取方法用于设置或返回对象的属性(也就是对象的实例变量)。由于属性的表示方法不同,我们提倡的存取方法的格式也有差异:

- (id)viewWithTag:(int)aTag;
page7image11336page7image11760page7image11920

正确

- (id)taggedView:(int)aTag;
page7image16544page7image16704page7image17296page7image17456page7image18368

错误

page7image20248
- (id)initWithFrame:(NSRect)frameRect;
page7image23104page7image23528page7image23688

NSView

page7image25472page7image25632page7image26224page7image26384
- (id)initWithFrame:(NSRect)frameRect mode:(int)aMode
cellClass:(Class)factoryId numberOfRows:(int)rowsHigh
numberOfColumns:(int)colsWide;
page7image29512

NSMatrix NSView 的子类。

page7image31760

- (int)runModalForDirectory:(NSString *)path file:(NSString *)name types:(NSArray *)fileTypes;

page7image34256page7image34680page7image34840

正确

- (int)runModalForDirectory:(NSString *)path andFile:(NSString*)name andTypes:(NSArray *)fileTypes;

page7image39656page7image39816page7image40408page7image40568page7image41800

错误

page7image42816page7image42976
- (BOOL)openFile:(NSString *)fullPath
withApplication:(NSString *)appName
andDeactivate:(BOOL)flag;
page7image46576page7image47000page7image47160page7image47912

NSWorkspace

page7image49792
page7image49952

如果某个属性使用名词来表示,则方法的格式如下:
page8image1952page8image2376
- (void)setColor:(NSColor *)aColor;
page8image4296page8image5208page8image5800page8image7064
- (NSColor *)color;

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

如果某个属性使用形容词表示, 则方法的格式为:

- (void)setAdjective:(BOOL)flag;- (BOOL)isAdjective;
例如:

 如果某个属性使用动词表示,则方法的格式为:

- (void)setVerbObject:(BOOL)flag;- (BOOL)verbObject;
例如:

这种情况下,动词应使用一般现在时的格式。
 请不要使用分词形式把动词转换为形容词:
page8image12096page8image12520
- (void)setEditable:(BOOL)flag;
page8image14392page8image15304page8image17072
- (BOOL)isEditable;

page8image19144page8image19568
- (void)setShowsAlpha:(BOOL)flag;
page8image21440page8image22352page8image24120
- (BOOL)showsAlpha;

- (void)setAcceptsGlyphInfo:(BOOL)flag;
page8image27600page8image28024page8image28184

正确

- (BOOL)acceptsGlyphInfo;
page8image32208page8image32368page8image32960page8image33120

正确

- (void)setGlyphInfoAccepted:(BOOL)flag;
page8image38200page8image38360page8image38952page8image39112

错误

page8image41056page8image41216page8image41808page8image41968
- (BOOL)glyphInfoAccepted;
page8image44824

错误

page8image46704

您可以使用情态动词(在动词前冠以“can”,"should","will"等),使得方法的名称更加明确,但是请不要使用“do”“does”这样的情态动词。

- (void)setCanHide:(BOOL)flag;
page9image3424page9image3848page9image4008

正确

page9image5792page9image5952page9image6544page9image6704
- (BOOL)canHide;

正确

- (void)setShouldCloseDocument:(BOOL)flag;
page9image12232page9image12392page9image12984page9image13144

正确

- (BOOL)shouldCloseDocument;
page9image18224page9image18384page9image19136page9image19296

正确

page9image21080page9image21240page9image21832page9image21992
- (void)setDoesAcceptGlyphInfo:(BOOL)flag;

错误

- (BOOL)doesAcceptGlyphInfo;
page9image28112page9image28272page9image28864page9image29024

错误

page9image31728

只有当方法间接地返回对象或者数值,您才需要在方法名称中使用 get"。这种格式只适用于需要返回多个数据项的方法。

如果方法格式和上面一样,则其实现应该能够接受 NULL 参数,这样调用者才能够表明他们对其中的一个或者多个返回值不感兴趣。

委托方法

委托方法是指当某些事件发生时,对象在委托里调用的处理方法(如果委托实现了它们)。委托方法的格式独特,但它也适用于在对象数据源里调用的方法:

方法名称的开头应标识出发送消息的对象所属的类:

在此,类的名称不需要使用前缀并且首字符要小写。
除非方法只有一个参数,并且该参数表示消息的发送者,否则类名称后面都要加上一个

   冒号(参数是委托对象的引用)。

如果是因为发送了一则通告而导致某个方法被调用,则上述原则不适用。在这种情况下,方法仅有的一个参数是通告对象。

- (void)getLineDash:(float *)pattern count:(int
*)count phase:(float *)phase;
page9image38144page9image38568page9image38728

NSBezierPath

page9image41272
page9image41432
page9image42288page9image43032
- (BOOL)tableView:(NSTableView *)tableView shouldSelectRow:(int)row;
page9image44720page9image45520
page9image46336
- (BOOL)application:(NSApplication *)sender openFile:(NSString
*)filename;
page9image47792
page9image48112 page9image48272 page9image48696 page9image48856 page9image49016 page9image49176 page9image49336 page9image49496 page9image49656 page9image49816
- (BOOL)applicationOpenUntitledFile:(NSApplication *)sender;
page9image50768 page9image50928 page9image51088 page9image51248
page10image560 page10image720 page10image880 page10image1304 page10image1464 page10image1624 page10image1784 page10image1944 page10image2104 page10image2264
- (void)windowDidChangeScreen:(NSNotification *)notification;
page10image3216 page10image3376 page10image3536 page10image3696

如果调用某个方法是为了通知委托某个事件已经发生或者即将发生, 则请在方法名称中使用“did”或者“will”这样的助动词。

如果调用某个方法是为了要求委托代表其他对象执行某件事,当然,您也可以在方法名称中使用“did”或者“will”,但我们倾向于使用“should”

集合方法

对于管理一个对象集合的对象(每个被管理的对象称为集合的一个元素),习惯上,我们要求它具有如下格式的方法:

- (void)addElement:(elementType)anObj;
- (void)remove
Element:(elementType)anObj;- (NSArray *)elements;
例如:

下述内容是该原则的条件和细化:

如果集合确实是无序的,则应返回一个 NSSet 类型的对象,而不是返回 NSArray 对象。

如果把元素插入到集合的指定位置这一功能很重要,则应使用与下面类似的方法来替换或者补充前述的某些方法。

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

使用集合方法时, 您需要记住下面这两条实现细节:

上述方法通常隐含了它们对于被插入对象的所有权,因此,用于添加或者插入对象的代码必须增加对象的计数,而用于移除对象的代码也必须要释放对象。

如果被插入的对象需要有一个指针指向其幕后的主对象, 则通常情况下, 您应该使用 set...这样方法,它可以设置对象的背后对象指针,但并不增加其引用计数。我们

page10image10064page10image10968
- (void)browserDidScroll:(NSBrowser *)sender;
page10image12856page10image13656page10image14296
- (NSUndoManager *)windowWillReturnUndoManager:(NSWindow *)window;
page10image15608
page10image15928 page10image16352 page10image16776 page10image17368 page10image18632
- (BOOL)windowShouldClose:(id)sender;
page10image19544
page10image19984
- (void)addLayoutManager:(NSLayoutManager *)obj;
page10image22624page10image23536
- (void)removeLayoutManager:(NSLayoutManager *)obj;
page10image25296page10image26208
page10image26920
- (NSArray *)layoutManagers;

insertLayoutManager:atIndex:方法为例,NSLayoutManager 使用如下方法来实现这一功能:

正常情况下, 您应该不会直接调用 setTextStorage:方法,但可能需要对其进行重写。

我们还有另外一个示列用于展示集合方法的上述约定,它来自于 NSWindow 类:

page11image2584page11image3328
- (void)setTextStorage:(NSTextStorage *)textStorage;
page11image5216page11image6016page11image6656
- (NSTextStorage *)textStorage;
page11image7928
page11image8912page11image9656
- (void)addChildWindow:(NSWindow *)childWin
ordered:(NSWindowOrderingMode)place;
page11image11720page11image12520
- (void)removeChildWindow:(NSWindow *)childWin;
page11image14168page11image14968
- (NSArray *)childWindows;
page11image16960page11image17760
page11image19152page11image19952
- (NSWindow *)parentWindow;
page11image21600page11image22400
page11image23216
- (void)setParentWindow:(NSWindow *)window;
page11image24488

方法的参数下面是数条和方法参数命名相关的通用规则:

page11image25400



和方法名称一样, 参数的名称也是以小写的字符开头,并且后续单词的首字符要大写。例如:removeObject:(id)anObject)。

请不要在参数名称中使用"pointer"或者"ptr"。您应该使用参数的类型来声明参数是否是一个指针。

 请不要使用一到两个字符的名称作为参数名。

请不要使用只剩几个字符的缩写。
习惯上
(Cocoa ),我们把下面的关键字和参数应该组合在一起使用:

page11image29288page11image29712
...action:(SEL)aSelector
page11image31568page11image32480
...alignment:(int)mode
page11image34368page11image35280
...atIndex:(int)index
page11image36960page11image37872
...content:(NSRect)aRect
page11image39872page11image40784
...doubleValue:(double)aDouble
page11image42672page11image43584
...floatValue:(float)aFloat
page11image45264page11image46176
...font:(NSFont *)fontObj
page11image48200page11image49112page11image49704
...frame:(NSRect)frameRect
page12image1160page12image1584page12image2176
...intValue:(int)anInt
page12image4192
...keyEquivalent:(NSString *)charCode
page12image6232page12image7144
...length:(int)numBytes
page12image9032page12image9944
...point:(NSPoint)aPoint
page12image11624page12image12536
...stringValue:(NSString *)aString
page12image14576page12image15488
...tag:(int)anInt
page12image17360page12image18272page12image18864
...target:(id)anObject
page12image20880
page12image21600page12image22864
...title:(NSString *)aString

私有方法

大多数情况下,私有方法遵循和公共方法一样的命名规则。但是,有一种常见的约定是为私有方法添加一个前缀,这样我们就很容易区分它们。但即便是利用这样的约定,私有方法的名称还是有可能导致奇怪的问题。当您为某个 Cocoa 框架类设计子类时,您无法知道您的某个私有方法是否在无意中覆盖了具有相同名称的私有的框架方法。

大部分 Cocoa 框架中私有方法的名称都带有一个下划线前缀(例如,_fooData ),这个前缀把方法标记为私有。根据这样的实际情况,我们给出两条建议:

请不要在您的私有方法中使用下划线作为前缀,因为苹果公司保留使用这种命名约定。

在为某个很大的 Cocoa 框架类(例如 NSView)派生子类时,如果需要绝对保证子类私有方法名称不会和超类发生冲突,则您可以为子类私有方法添加自己的前缀。前缀应该尽可能地具有唯一性,也许您的前缀可以基于公司或者项目名称,并且使用"XX_"这样的格式。例如,如果您的项目叫做 Byte Flogger,则前缀可以是 BF_addObject:这样的格式。

尽管为私有方法名称添加前缀似乎和早前我们对类方法的命名要求相矛盾,但这是因为此处的目的和早前不同:我们这么做是为了避免在无意中重写了超类中的私有方法。

为函数命名

Objective-C 允许使用函数或者方法来表达行为。如果底层对象总为单例或者处理的事物明显是功能性子系统,则您应该使用函数而非类方法。

请遵守下述几条函数通用命名规则:
函数名称和方法名称格式相似,但是有两种情况例外:

函数要使用前缀开头,并且这个前缀和类或者常量所使用的一样。

前缀后面的单词首字符要大写。
大多数函数名称以动词开头,该动词描述了函数的作用:

page12image29088
page13image1384page13image1808
NSHighlightRect
page13image3648page13image4560page13image5152page13image6416
NSDeallocateObject

用于查询属性的函数有一套更细致的命名规则:
如果函数返回其首个参数的某个属性,则请省略掉函数名称中的动词。

      float NSHeight(NSRect aRect)

如果函数返回的值是个引用,则请在函数名称中使用“Get”

如果函数返回值是布尔类型,则它应以曲折动词开头。

为实例变量和数据类型命名本节描述实例变量、常量、异常、以及通知的命名约定。

实例变量在为某个类添加实例变量时,请记住下面几个因素:

避免创建公共实例变量。开发人员应该关心对象的接口,而不是对象的数据存储方式这样的细节。

请把实例变量显式声明为@private 或者@protected。如果您预期所提供的类会被子类化,并且子类可能需要父类的数据, 则请使用@protected指令来修饰实例变量。

请确保实例变量的名称能够扼要地描述它所保存的属性。如果实例变量将作为类对象的可访问属性,则请务必为其编写存取方法。

常量根据常量创建方式不同,其命名规则也有所差异。

page13image11448 page13image12040 page13image12200
unsigned int NSEventMaskFromType(NSEventType type)
page13image12928 page13image13520 page13image13680 page13image13840 page13image14264 page13image14424 page13image14584 page13image14744 page13image14904 page13image15064 page13image15224
const char *NSGetSizeAndAlignment(const char *typePtr, unsigned int
*sizep, unsigned int *alignp)
page13image16600 page13image16760 page13image16920 page13image17080 page13image17240 page13image17400 page13image17560 page13image17984 page13image18144 page13image18304 page13image18464 page13image18624 page13image18784 page13image18944
BOOL NSDecimalIsNotANumber(const NSDecimal *decimal)
page13image19936 page13image20096 page13image20256 page13image20416 page13image20576 page13image20736

枚举常量
请使用枚举类型来表示一群相互关联的整数值常量。

枚举常量及其所属的通过typedef定义的数据类型遵循和函数一样的命名约定(请查看为函数命名一节)。下面是一个取自NSMatrix.h文件的例子

page14image2560page14image2984
typedef enum _NSMatrixMode {
page14image4960page14image5872

NSRadioModeMatrix = 0,

page14image7824page14image8736page14image9328

NSHighlightModeMatrix = 1,

page14image11424
NSListModeMatrix          = 2,
page14image13520page14image14432page14image15024

NSTrackModeMatrix = 3

page14image17120
page14image17840page14image19104
} NSMatrixMode;

请注意,在上述例子中,typedef 标签不是一定要具有的。
您也可以创建匿名的枚举类型来表示诸如位掩码这样的事物。例如:

page14image21488page14image21912

enum {

page14image23840page14image24752
NSBorderlessWindowMask     = 0,
page14image26512page14image27424

NSTitledWindowMask = 1 << 0,

page14image29584page14image30496

NSClosableWindowMask = 1 << 1,

page14image32544page14image33456

NSMiniaturizableWindowMask = 1 << 2,

page14image35296page14image36208

NSResizableWindowMask = 1 << 3

page14image38376page14image39288page14image39880page14image41448
page14image42168page14image43432

};

使用 const 创建的常量
请使用 const 来创建浮点值常量。如果某个整数值常量和其他的常量不相关,您也可以

使用 const 来创建,否则,则应使用枚举类型。下面的声明展示了 const 常量的格式:

      const float NSLightGray;

使用枚举类型声明的常量遵循和函数相同的命名约定。(请参考为函数命名一节)。

其他类型的常量

通常情况下, 请不要使用#define 预处器理命令创建常量。对于整数值常量,请使用枚举类型创建,而对于浮点值常量,请使用 const 修饰符创建,这和前述的原则一样。

page14image47184

有些符号,预处理器需要对其进行计算,以便决定是否要对某一代码块进行处理,则它们应该使用大写字符表示。例如:

请注意,编译器定义的宏,其开头和结尾要具有两个下划线字符。例如:

对于通告名称或字典关键字的字符串,请将其定义为常量。通过使用字符串常量,编译器可以验证字符串是否被正确赋值(也就是说, 编译器将执行拼写检查)。Cocoa 框架提供很多字符串常量的例子,例如:

在实现文件中,NSString 的实际值被指定为常量。(请注意,APPKIT_EXTERN 宏经过计算之后是 Objective-C 中的 extern 关键字)。

异常和通告异常和通告的名称遵循相似的命名规则,但是我们为二者推荐的使用模式并不相同。

异常

尽管您可以随意地将异常(即 NSException 类和一些相关联的函数所提供的机制)用于任何目的,但是通常情况下,cocoa 不会利用他们来处理常规的、可预期的错误条件。这类错误应使用诸如 nilNULLNO 这样的返回值或者错误码来表示。通常,Cocoa 把异常用于表示诸如数组索引越界这样的编程错误。

异常使用全局的 NSString 对象来标识,其名称按如下的方式进行组合:

异常名称中的具有唯一性的那部分,其组成词应该拼写在一起, 并且每个单词的首字符要大写。下面是一些例子:

page15image5288 page15image5712 page15image6136 page15image6728 page15image7992

#ifdef DEBUG

page15image8904 page15image9328 page15image9752 page15image10344 page15image11608

__MACH__

page15image12480 page15image12904 page15image13328 page15image13920 page15image15184
APPKIT_EXTERN NSString *NSPrintCopies;
page15image16136 page15image16296 page15image16720 page15image17144 page15image18912
[Prefix] + [UniquePartOfName] + Exception
page15image20680page15image21104
NSColorListIOException
page15image22960page15image23872
NSColorListNotEditableException
page15image25744page15image26656page15image27248
NSDraggingException
page15image29264
NSFontUnavailableException
page15image31248page15image32160page15image32752page15image34016
NSIllegalSelectorException

通告

如果某个类含有委托,则通过所定义的委托方法,类的委托可以收到大部分通告。通告的名称应该反映相应的委托方法。例如,一旦应用程序发送一则NSApplicationDidBecomeActiveNotification 的通告,则全局 NSApplication 对象的委托就会自动进行注册,这样它就可以接收到一条 applicationDidBecomeActive:的消息。

通告使用全局的 NSString 对象进行标识,其名称按如下的方式组合:

例如:

 NSApplicationDidBecomeActiveNotification
 NSWindowDidMiniaturizeNotification
 NSTextViewDidChangeSelectionNotification
 NSColorPanelColorDidChangeNotification

可以使用的缩略名称

设计编程接口时,通常不应使用名称缩写(请参考 通用规则一节)。但是下列缩写可以继续用,因为它们要么已得到广泛认可,要么从过去就开始使用了。另外,请注意下面两件事情,它们也跟名称缩写有关:

标准 C 库里已使用很长时间的缩写格式例如,“alloc”“getc”—可以复制到 Cocoa 中。

参数名称可以更加随意地使用缩写(例如,“imageRep”“col” (表示“column”)“obj”、以及“otherWin”)。

page16image4648 page16image4808 page16image4968 page16image5392 page16image5552 page16image5712 page16image5872 page16image6032 page16image6192 page16image6352

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

page16image7664 page16image7824 page16image7984 page16image8144 page16image8304
page16image9224page16image9816

缩写

page16image11224page16image11816page16image11976page16image12568
page16image12904page16image13488page16image13808

意义和注释

page16image15168

alloc

分配内存

page16image18104page16image18696page16image19016page16image19608

alt

page16image20856page16image21448page16image21608page16image22200
page16image22704page16image23024page16image23344

可选的

page16image24704

app

应用程序。例如,NSApp 表示全局的应用程序对象。但是在委托方法中,需要把“application”整个拼写出来。

page16image27792page16image28384page16image28704page16image29296

calc

page16image30864page16image31184

计算

page16image32560page16image33152page16image33472page16image34064

dealloc

page16image35632page16image35952

释放内存

page16image37360page16image37952page16image38272page16image38864

func

page16image40112page16image40864
page16image41960page16image42280

函数

page16image44256

horiz

水平的

page17image624
page17image11328page17image11752page17image12072page17image12232

信息

page17image13856page17image14176page17image14336page17image14496page17image14816page17image14976
初始化(表示初始化新对象的方法)
page17image16488page17image16808page17image16968page17image17128page17image17448page17image17608

整数

page17image19472page17image19792page17image19952page17image20272page17image20592page17image20752

最大值

page17image22376page17image22696page17image22856page17image23016page17image23336page17image23496

最小值

page17image25008page17image25328page17image25488page17image25648page17image25968page17image26128

消息

page17image27992page17image28312page17image28472page17image28792page17image29112page17image29272

Interface Buidler 档案

page17image30976page17image31296page17image31456page17image31616page17image31936page17image32096

粘贴板(只能粘贴常量)

page17image33608page17image33928page17image34088page17image34248page17image34568page17image34728

矩形

page17image36592page17image36912page17image37072page17image37232page17image37552page17image37712

表现形式(用于诸如 NSBitmapImageRep 这样的类)

page17image39576page17image39896page17image40056page17image40216page17image40536page17image40696

暂时性

page17image42208page17image42528page17image42688page17image42848page17image43168page17image43328
page17image44320

垂直

page17image45192page17image45512page17image45672

info

init

int

max

min

msg

nib

pboard

rect

Rep

temp

vert

您可以用计算机行业中很常见的缩略语来代替其所表示的单词。下面是一些知名度比较高的简
写:

ASCII

PDF

page17image57112page17image57272page17image57864

XML

page17image59248page17image59672

HTML

URL

RTF

page17image64992page17image65152page17image65744page17image65904page17image66064page17image66656

HTTP

page17image68048page17image68472

TIFF

JPG

GIF

page17image73936page17image74096page17image74688page17image74848page17image75008page17image75600

LZW

page17image77104page17image77528

ROM

RGB

CMYK

page17image84296page17image84456page17image85048page17image85208page17image85368page17image85960page17image86280

MIDI

page17image87816page17image87976
page17image88440page17image88864page17image89448

FTP

框架开发者可以使用的技巧和技术

相对于其他的开发者而言,框架开发者要更加注意编写代码的方式。因为许多客户应用程序可能链接到框架,而这样宽泛地暴露接口,就导致框架的任何缺点都可能通过系统放大。下面的条款讨论一些框架编程技术,框架开发者可以利用它们来确保框架的高效性和完整性。

请注意:此处所讨论的一些技术并不局限于框架开发。将之用于应用程序开发同样卓有成效。

初始化

下述意见和建议涵盖框架初始化方面的内容。

类的初始化

initialize类方法中的代码只执行一次,它是类里面第一个被调用的方法。我们通常利用该方法来设置类的版本号(请参考版本化和兼容性一节)。对于继承链中的每一个类,不论其是否实现 initialize 方法,运行时都会向它发送 initialize消息。这可能导致一个类的 initialize 方法被多次调用(举个例子,如果子类没有实现initialize 方法,则其父类的方法将被调用两次)。但通常您希望始化代码仅执行一次,为确保如此,您可以执行如下检查:

您不应该显式地调用 initialize 方法。如果需要触发初始化行为,则请调用一些无害的方法,例如:

指定初始化函数

指定初始化函数是类的一个 init 方法,它调用超类的某个 init 方法(其他的初始化函数调用类自己定义的 init 方法)。每个公共类都应包含一个或多个指定初始化函数。举些例子,NSViewinitWithFrame:方法以及NSResponderinit方法都是指定初始化函数。在某些情况下,类的 init 方法并不想被重载,比如 NSString 和其他面向类簇的抽象类,因而其子类应该实现自己的初始化方法。

您应该明确标示出指定初始化函数,因为该信息对于想根据您的类来派生子类的开发者有重要的意义。一个子类可以只重载指定初始化函数,这对其他所有初始化函数没有影响,它们仍将按其原先设计的行为工作。

在实现一个框架类时,您经常需要为其实现诸如 initWithCoder:以及 encodeWithCoder:这样的归档方法。请注意,对象解档时未发生的事情不要放在初始化代码路径里执行。对于实现归档功能的类,我们有一个比较好的方法可以做到这一点,那就是在类的指定初始化方法以及initWithCoder: 方法(该方法也是个指定初始化函数)中调用一个公共的例程。

初始化过程中的错误检测为确保能够恰当地检测并传播错误,一个设计良好的初始化方法应完成如下步骤:

page18image6336 page18image6496
page18image7376page18image7800
if (self == [NSFoo class]) {
page18image9856page18image10768
// the initializing code
page18image12720page18image13632page18image15400

}

page18image16272 page18image16696 page18image17120 page18image17712 page18image18976
[NSImage self];

1.2.

3.

调用 super init 方法对 self 重新赋值。
检测指定初始化方法的返回值是否为
nil。返回值为 nil 表明超类的初始化过程出现错

误。
如果当前类在初始化的过程中出现错误,则请释放对象并且返回
nil 值。

列表 1 描述的方法可以完成上述步骤。列表 1 初始化过程中的错误检测

page19image3344
page19image4200page19image4944

- (id)init {

page19image6800page19image7600

if ((self = [super init]) != nil) { // call a designated initializer here

page19image9960page19image10760

// initialize object ...

page19image12448page19image13248
if (someError) {
page19image15248page19image16048
[self release]; // [self dealloc] or [super dealloc] might be
page19image18248page19image19048

self = nil; // better if object is malformed

page19image20936page19image21736

}

page19image23656page19image24456

}

page19image26296page19image27096

return self;

page19image28704page19image29504
page19image30320

}

page19image31512

版本化和兼容性

在向框架添加新类或新方法时,您通常没必要为每个新功能群指定新的版本号。因为一般情况下,开发者会执行(或者说,应该执行)诸如 respondsToSelector:这种 Objective-C 的运行时检测来判断给定的系统是否存在某种功能。开发者比较喜欢使用这种方式来检测新功能,同时它也是最动态的方式。

不论如何,您可以使用数种技术以确保新版本的框架能被正确标志并尽可能地兼容早期版本。

框架的版本

如果已存在的新功能或者错误改正不容易通过运行时进行检测,则您应该为开发者提供检测这些变更的办法。有一种办法是把确切的框架版本号保存起来,然后让该号码对开发者可见:

把变更归档在一个版本号下面(例如,归档在发布记录中)。
设置框架的当前版本号并且提供某种方法使之全局可见。您可以把版本号保存在框架的

信息属性列表(Info.plist),这样就可以从该列表获取版本号。

基于键的归档

page19image34992

如果框架对象需要被写入到 nib 文件,则它们必须能够自我归挡 。另外,如果文档使用归挡机制来保存文档数据,则您也要对它们做归挡。在归挡时,您可以使用老风格(利用initWithCoder:encodeWithCoder:这样的方法)。但是,为了更好地兼容过去、现在、以及未来的框架版本,您应该基于健进行归档。

基于键进行归档,对象就可以使用键来读取或写入被归挡值。相对以往的归挡机制,该方法可以在前向和后向兼容性上提供更多的灵活性。因为老归档机制要 求代码和读取或写入的值维持相同的顺序,而且它也没有什么好办法来改变已写到档案的数据。如果您需要了解更多基于键的归档机制,请参考 Cocoa归档和序列化编程指南 (Archives and Serializations ProgrammingGuide for Cocoa)一文。

对于正在编写的新的类,请为其使用基于键的归档机制。如果之前已发布的类使用了老归档机制,您也无需再采取任何措施。如果对象实现了 Mac OS X 10.2 版本之前的归档机制,则它必须能从档案中读取内容并能其内容写入到档案。但如果您在 Mac OS X v10.2 及之后的平台上为该对象添加新属性,则您不必,实际上是不应该,将这些属性保存到老档案中(这样做可能会使老档案在更早的系统中变得不可读 取),这种情况下,新属性应使用键值归档机制。

请注意下列和基于键归档相关的事实:
page20image4136 page20image4296 page20image4456



如果档案中的某个键丢失,则在获取这个键对应值的时候,依据所要求的类型,其返回值可能是 nilNULLNO0、或者 0。通过对该返回值进行测试,您可以减少写到档案中的数据。同时,还可以检测某个键是否已被写入到档案中。

如果使用旧式归档,则 initWithCoder:的 实现需要独自挑起兼容性的重担。但如果使用键值归档,则归档方法和解档方法都可以采取一些措施以保证兼容性。举个例子,一个新版本的类的归档方法可能使用 键来写入一个新值,而依旧把早期的字段写入到档案中,这样类的旧版本仍然可以理解该对象。与此同时,我们还可以在解档方法中使用某种合理的方式以处理数值 缺失的情况,从而为将来的版本保留一些灵活性。

在命名框架类的档案键时,我们提倡使用和框架其他 API 元素一样的前缀,前缀后面再使用实例变量名称。您只要确保它的名称不会和任意的子类或者超类名称发生冲突即可。

如 果您使用一个工具函数向档案中写入一个基本数据类型(换句话说,就是一个非对象的值),则请务必为该数值使用一个唯一键。举个例子,如果您有一个对矩形归 档的“archiveRect” 例程,则您应该为该函数传入一个键作为参数。您可以直接把它作为档案键;或者,如果这个例程向档案写入多个值(例如,写入四个浮点数值),则它应该把每个 数值自身独有的位添加到所提供的键上面。

位字段对于编译器和比特序有依赖关系,按照位字段现有的格式进行归档可能会有危险。只有当有多个位元需要被写入档案多次时,我们才会对位字段进行归档,这主要是为了提高性能。请参看 位字段一节 以获取相关的建议。

page20image9824

对象的尺寸和保留字段

每个 Objective-C 的对象都有一个尺寸,它由对象自身实例变量加上对象所有超类具有的实例变量得到的总尺寸决定。如果改变了一个类的尺寸, 则其拥有实例变量的子类必须重新编译。为

了保持二进制兼容性,通常情况下,我们不能通过向类添加新的实例变量或者去除类里面不必要的实例变量来改变对象的 尺寸。

因此,对于新的类,为其留下几个额外的保留字段以便于将来扩展是个不错的想法。如果一个类只会有少数的几个实例,这个想法显然不成问题。但如果这个类会被实例化数千次,则您可能需要让保留的单个变量的尺寸小一些(也就是说,任意对象都占用四个字节)。

对于较早的类的对象,如果它们的空间已经用完(并且假定实例变量没有被导出成为公共变量),则您可以移动实例变量,或者把它们捆绑在一起,使之成为 一个更小的字段。通过对实例变量进行重新排布,您就有可能添加新的数据而不会导致对象的总尺寸发生改变。或者您可以把一个剩余的保留槽作为指针,使它可以 指向一块额外的内存,然后您在对象初始化的时候分配这块内存(并且在对象释放的时候销毁它)。又或者您可以把额外的数据放入到一张外部的哈希表(例如放入 NSDictionary);这种方法对于那些很少创建使用的实例变量具有很好的效果。

异常和错误

大多数 Cocoa 框架的方法不会强制要求开发者捕捉处理异常,因为程序正常执行时不会产生异常,而且我们通常也不使用异常来表示可预期的运行时错误或用户错误。下面这些例子属于可预期的运行时错误或者用户错误:

文件找不到
用户不存在
试图打开应用程序中一个错误类型的文档把字符串转化为特定编码时出现错误

但是不管怎么样, Cocoa 确实会引发异常以指示下面这些编程错误或者逻辑错误:数组索引越界
试图改变一个不可改变的对象
错误参数类型

我们认为应用程序推向市场之前,开发者会对其进行测试,发现并解决这些类型的错误。因此,应用程序不需要在运行时处理上述异常。如果出现一个异常发 生但应用程序没有捕捉,则最顶层的缺省处理器通常会捕捉并且报告该异常,然后程序将继续执行。开发者可以选择替换掉这个缺省的异常处理器,新的处理器可以 更详细地描述什么地方发生了错误,并且还能够让用户选择是否保存数据并且推出应用程序。

错误是 Cocoa 框架不同于其他软件库的又一个地方。Cocoa 的方法通常不返回错误码。某些情况下,一个错误具有一个合理或者可能的原因,方法通过对一个布尔值或者对象返回值(nil/non-nil)进行简单测试以判断该情况;但是返回 NO 或者 nil 值的原因则被记录在文档中。另外,您不应该使用错误码来指示需要在运行时处理的编程错误,相反您应该引发一个异常,或者在某些情况下,您也可以只记录下该错误而不引发异常。

page21image7288

举个例子,NSDictionary objectForKey:方法会返回所找到的对象,如果对象找不到,则它会返回 nil 值。NSArray objectAtIndex:法不可能返回 nil 值(除非我们把通用的编程语言约定重载成向 nil 对象发送消息都会返回 nil 值),因为 NSArray 对象不能保存 nil 值,并且所有的越界访问都被定义成编程错误,这种错误引发异常而并不返回 nil 对象。如果对象不能使用用户提供的参数进行初始化,许多 init 方法都会返回 nil

在少数情况下,一个方法需要多个不同的错误码是合理的。这时,该方法需要将错误码指定到一个传引用参数,然后您可以利用该参数返回一个错误码,也可 以返回一个本地化的错误字符串,或者一些其他的可以描述错误的信息。举个例子,您可以把错误转换成 NSError 对象返回(请参看 Foundation 框 架的 NSError.h 头文件以获取更多的细节)。而除了这个 NSError 对象,方法还能直接返回相对简单的 BOOL 值或者 nil 值。另外要注意,这种方法的所有传引用参数都是可选的。因此,如果发送者不想了解错误原因,它们可以传一个 NULL 值给错误码参数。

重要: NSError 类在 Mac OS X v10.3 之后的版本对外公开。

框架数据

框架数据的处理方式可能会对性能、跨平台兼容性、以及其他某些方面产生影响。本节讨论一些和框架数据相关的技术。

常量数据

出于性能的原因,您应尽可能多地把框架数据标志为常量,因为这样可以减小 Mach-O 二进制文件的__DATA 段的尺寸。没有 const 标记的全局或静态的数据最终会存放在__DATA 段的__DATA 节。对于每一个使用该框架的应用程序实例,这种类型的数据都会占用内存。尽管多出500 字节(举个例子)好像没那么糟糕,但是这有可能导致应用程序所需的内存页面的数量增多即每个应用程序都需要额外的四千字节的内存。

您应该为所有不变的数据添加 const 标记。具有 const 标记的数据块,如果其中没有 char *类型的指针,则该数据会被存放在__TEXT 段中(这会使数据成为真正的常量); 否则数据就会被存放在__DATA 段中,但是这些数据不可以被写入(除非预绑定未完成,如果预邦定已经完成,则只能在加载的时候,通过移动二进制文件的方式来写入)。

您应该初始化静态变量,这样可以确保该变量被合并到__DATA 段的__data 节中而非__bss 节。如果没有明显的值用于初始化静态变量,则请使用 0NULL0.0、或者任何恰当的值。

位字段

如果使用有符号的值来表示位字段,而代码又假定这个位字段是布尔值,则可能会导致未定义的行为。只有一个位的位字段尤为如此。因为在这种情况下,这 个位字段只能存储 0 -1(取决于编译器的实现),把它和 1 做比较,其结果总是不相等。因此,只有一个位的位字段应该是无符号的。举个例子,如果您在代码中遇到如下情况:

page22image9456
page23image1816page23image2240
BOOL isAttachment:1;
page23image4120page23image5032page23image5624page23image6888
int startTracking:1;

您应该把上述代码中的类型改为 unsigned int和位字段相关的另一个问题是归档。通常情况下,您不应该按照当前格式将其写入到磁盘或者档

案中,因为当我们在另外的架构或者编译器中读取这些字段时,它们的格式可能发生变化。

内存分配

在框架代码中,如果可以的话,最好是完全避免分配内存。如果出于某种原因,您需要一块临时的缓冲区,则通常情况下,使用栈比分配缓冲区更好。但是, 栈的大小有限(栈总的大小通常为 512 千字节), 因此是否使用栈取决于函数和您所需要的缓冲区的大小。通常情况下,如果您需要的缓冲区的大小不超过 1000(或者是 MAXPATHLE 定义的值)字节,使用栈是合适的。

一个比较精细的方法是在开始的时候使用栈,但是如果所需要的内存的大小超过了栈缓冲区的大小,则切换到malloc内存分配方式。列表 2展示的代码片段就是这么做的。

列表 2 使用栈内存和通过 malloc 分配的缓冲区
#define STACKBUFSIZE (1000 / sizeof(YourElementType))

YourElementType stackBuffer[STACKBUFSIZE];
YourElementType *buf = stackBuffer;
int capacity = STACKBUFSIZE; // In terms of YourElementTypeint numElements = 0; // In terms of YourElementType

while (1) {
if (numElements > capacity) { // Need more room

intnewCapacity=capacity*2; //Orwhateveryourgrowthalgorithmis

if(buf==stackBuffer){ //Previouslyusingstack;switchtoallocatedmemory

           buf = malloc(newCapacity * sizeof(YourElementType));

memmove(buf, stackBuffer, capacity * sizeof(YourElementType));} else { // Was already using malloc; simply realloc

           buf = realloc(buf, newCapacity * sizeof(YourElementType));
        }
        capacity = newCapacity;
    }
    // ... use buf; increment numElements ...
   }

// ...

page23image17488 page23image17648 page23image17808 page23image17968 page23image34016 page23image44352 page23image44512 page23image44672 page23image44832 page23image44992 page23image45152 page23image45312 page23image45472 page23image45632 page23image45792 page23image45952 page23image46112 page23image46272 page23image46432 page23image46592 page23image46752 page23image46912 page23image47072 page23image47232 page23image47392 page23image47552 page23image47712 page23image47872 page23image48032 page23image48192 page23image48352 page23image48512 page23image48672 page23image48832 page23image48992 page23image49152 page23image49312 page23image49472 page23image49632 page23image49792 page23image49952 page23image50112 page23image50272 page23image50432 page23image50592 page23image50752 page23image50912 page23image51072 page23image51232 page23image51392 page23image51552 page23image51712 page23image51872 page23image52032 page23image52192 page23image52352 page23image52512 page23image52672 page23image52832 page23image52992 page23image53152 page23image53312 page23image53472 page23image53632 page23image53792 page23image53952 page23image54112 page23image54272 page23image54432 page23image54592 page23image54752 page23image54912 page23image55072 page23image55232 page23image55392 page23image55552 page23image55712 page23image55872 page23image56032 page23image56192 page23image56352 page23image56512 page23image56672 page23image56832 page23image56992 page23image57152 page23image57312 page23image57472 page23image57632 page23image57792 page23image57952
page24image496 page24image656 page24image816 page24image1240 page24image1400 page24image1560 page24image1720 page24image1880 page24image2040 page24image2200
if (buf != stackBuffer) free(buf);
page24image3232 page24image3392 page24image3552 page24image3712

语言问题
下面的条款讨论和 Objective-C 语言相关的问题,包括协议、对象比较、以及向对象发送

autorelease 消息的时机。

nil 对象发送消息

Objective-C 中,只要消息的返回值是对象、任意的指针类型、或是其尺寸小于等于sizeof(void*)的整数型标量,我们就可以将其发送给 nil 对象,该消息将返回 nil 值。这个特性一种很有价值的编程资产,但是有一个问题需要注意。如果发送给 nil 对象的消息的返回值非上述类型(例如,消息返回任意的 struct 类型,或者浮点类型,或者任意的向量类型),则消息的返回值未定义。通常情况下,如果消息的返回值不是一个对象,则不要依赖这种行为,因为那是很糟糕的做法。在 Power PC 系统中,向 nil 对象发送上述类型的消息不会有问题,但对于其他架构来说,这种行为行不通。

对象比较

通用的对象比较方法 isEqual:和与对象类型关联在一起的比较方法(诸如 isEqualToString:)有一个重要的不同。isEqual:方法允许任何对象作为参数,如果用于比较的对象属于不同的类,则该方法将返回 NO 值。而诸如 isEqualToString:以及 isEqualToArray:这样的方法通常都假定参数是某种特定的类型(和接收者一样的类型)。因此,这种函数不会执行类型检查,这使得它们运行速度比普通的对象比较方法快,但是安全性则不如之。对于那些从外部源获取的值,例如从应用程序的的信息属性表(Info.plist)或者应用程序的偏好设置获取的值,在对其进行比较时,我们倾向于使用 isEqual:方法,因为这种方法更安全;但如果我们已知道要进行比较的值的类型,则应使用 isEqualToString:方法。

关于 isEqual:方法,还有一点就是它和 hash 方法的联系。存放在基于散列的 Cocoa 集合类里的对象(例如存放在 NSDictionary 或者 NSSet 中的对象)有一个基本的不变式,即如果[AisEqual:B] == YES,则[A hash] == [B hash]也成立。因此,如果您重写了类的 isEqual:方法,则您也应重写 hash 方法以保持不变式成立。缺省情况下,isEqual:方法判断对象地址指针是否相等,而 hash 方法则返回一个基于对象地址产生的 hash 数值,因此,这个不变式就可以保持成立。

协议

协议是 Objective-C 中一个有趣的概念,但是它们在 Cocoa API 中用得有限。之所以这样做,有一个原因是协议有严格的设计要求。以某个含有十个方法的 NSDataSource 协议为例。如果某个开发人员遵循该协 议并且实现其所有方法,而之后您又向该协议添加了一个新的方法,这就

page24image11712

会破坏开发者和原有协议的一致性。因此,协议往往仅能包含它们首次公开时所含有的方法 集(除非您不期望其他开发者实现该协议)。因此,只有方法集不可能再增长时,才应使用协议。如果您必须扩展某个协议,则您应该添加一个新的协议,利用新协 议来对原来的协议进行扩展,或者您可以把新方法添加在协议外部,并且在使用前检查它是否存在。

基本上,上述的原因也可以解释为何您不能使用常规协议来声明委托方法。而把委托方法声明为NSObjec 上的范畴类即非正式的协议的另一个原因是我们可以选择是否实现某个方法。

自动释放的对象

返回对象值的方法或者函数,如果它们不是用于创建对象或者复制对象(new,alloc,copy 以及这些方法的变体),则请务必让其返回的对象可以 autoreleased 。在此处,自动释放并不一定表示对象应该显式自动释放-即在返回对象之前,向对象发送 autorelease 消息。一般情况下,它仅表示返回值不是由调用者释放。

出于性能方面的原因,请尽可能不要在方法实现中使用自动释放的对象,特别是那些可能会在短时间内频繁执行的代码(例如在循环结构中,循环次数未知并且可能是很多的情况)使用。举个例子,对于下面的情况,您不应该发送如下的消息:

而是应该发送下面的消息:

当您不再需要该字符串对象时,请显示地释放它。但是,请记住有些时候方法或者函数会返回自动释放的对象,这时您需要向对象发送 autorelease 消息。

存取方法

在存取方法中作什么事情才是正确呢,这是个重要的问题。举个例子,假设您在一个获取方法中直接返回一个实例变量,而后立刻调用设置方法,则在释放先 前实例变量的时候就可能会有危险,因为它有可能把您之前返回的值给释放掉。Cocoa 框架的原则是让设置方法自动释放实例变量先前的值,但某些情况下,所 涉及的某个设置方法会被频繁的调用(例如在一个很密集的循环中),这时候 Cocoa 的原则就不适用了。但在实践中,这种情况非常罕见,除非是一些底层的对 象才会如此。另外,诸如 NSAttributedStringNSArray、以及 NSDictionary 这样的通用集合不会自动释放对象,这主要是 为了维护对象的存在时间。它们只是简单地留存或者释放其含有的对象。另外,它们也应该对这一事实进行归档,这样客户程序就可以了解这些对象的行为。

对于现在正在编写的框架代码, 我们建议在 get 方法中使用自动释放的对象,因为这是最安全的方法:

page25image6800 page25image7224 page25image7648 page25image9416
[NSString stringWithCharacters:]
page25image10328 page25image10752 page25image11176 page25image12944
[[NSString alloc] initWithCharacters:]
page25image14176
- (NSString *)title {
page25image16824page25image17736
return [[instanceVar retain] autorelease];
page25image19688page25image20600page25image21192

}

page26image696page26image3456
- (void)setTitle:(NSString *)newTitle {
page26image5576page26image6488
if (instanceVar != newTitle) {
page26image8536page26image9448
[instanceVar release];
page26image11168page26image12080
instanceVar = [newTitle copy];
page26image14200page26image15112
// or retain, depending on object & usage
page26image17264page26image18176page26image18768

}

page26image20784
page26image21496

}

另外,我们还需要考虑设置方法是使用 copy 方式还是使用 retain 方式。如果您所感兴趣的是对象的值而非实际对象本身,则请使用 copy 方式。一个一般性的经验法则是对实现 NSCopying协议的对象使用 copy 方式(您不应该在运行时检测对象是否实现 NSCopying 协议,而应该直接查找参考文档)。通常情况下,诸如字符串、颜色、URL 这样的对象应该能被复制;而像视图、窗口这样的对象则应该可以被保持。而至于其他的对象(例如数组),是使用 copy 还是使用retain,则要根据具体情况决定。

文档修订历史下表描述了 Cocoa 编码原则的变更。

page26image26456page26image27048

日期

page26image28296page26image28888page26image29048page26image29640
page26image29968page26image30552page26image30712

记录

page26image32072

2006-04-04

修改实例变量的相关原则并阐明了向 nil 发送消息的结果。把标题修改为"编码指南" 。

page26image35240page26image35832page26image36152page26image36744

2005-07-07

page26image38312page26image38632

修正错误.

page26image40008page26image40600page26image40760page26image41352

2004-07-23

page26image43080page26image43400

各种错误修正。

page26image44736page26image45328page26image45488

2003-04-28

page26image48720page26image49040page26image49520

编码指南第一版。

page26image50552

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值