Cocoa程序添加行为 使用Cocoa框架
Cocoa程序添加行为 使用Cocoa框架是本文丫介绍的内容,当您用Objective-C开发程序的时候,很多工作不必自己做。您要关注苹果和其它人已经完成的工作,关注他们已经开发完成并封装在Objective-C框架的类。这些框架为您提供一组独立的类,而这些类构成了您的程序的一部分—通常是很重要的一部分。
库函数很少对使用它们的程序进行限制,您可以在任何需要的时候进行调用。另一方面,面向对象的库或框架中的方法和类的定义紧密相关,如果您没有创建或保留可以访问那些定义的对象,就不能对其进行调用。而且,在大多数程序中,对象必须至少和一个对象相连接,才能在程序网络中发挥作用。一个类只负责定义一个程序组件,为了访问类提供的服务,您必须将它连接到应用程序结构中。
也就是说,框架类生成一些行为类似于一组库函数的实例。您简单地创建一个实例,对其进行初始化,然后或者向它发送消息使其完成某个任务,或者将它插入到应用程序中某个设计好的插槽中。举例来说,您可以用NSFileManager类来执行各种文件系统操作,比如移动、拷贝、和删除文件。如果您需要显示一个警告对话框,则可以创建一个NSAlert类的实例,并向它发送正确的消息。
然而在一般情况下,象Cocoa这样的环境并不仅仅是一些提供服务的、彼此独立的类集合。它们是由一些面向对象的框架和面向特定问题空间并提出完整解决方案的类集合组成的。框架不是提供一些在需要时可以调用、彼此不相关的服务(象函数库那样),而是制订您的代码必须适应的整个程序结构—或者说程序模型。由于这个程序模型是具有一般意义的,您可以对它进行具体化,以满足特定的程序需要。您要做的不是设计一个调用库函数的程序,而是将您自己的代码插入到框架提供的设计中。
要使用框架,就必须接受框架定义和使用的程序模型,而且需要定制一些类,使面向具体应用场合的程序可以和该模型相适应。这些类相互依赖,以一个组而不是单独类的形式出现。乍一看,在程序代码中采纳框架的模型需要做的工作比较有限,但事实却相反。框架为您提供了很多改变和扩展其一般行为的途径,它只是简单地要求您接受所有Cocoa程序的基本行为方式,因为它们都基于同样的程序模型。
框架类的类型
Cocoa框架中的类以四种方式发布它们的服务:
复活方式。一些类定义了复活(off-the-shelf)对象以方便用户使用。您可以简单地创建这些类的实例,并根据需要对其进行初始化。NSControl的子类,比如NSTextField、NSButton、和NSTableView(和与之相关联的NSCell类一起)都属于这个范畴。虽然您也可以通过编程的方式来创建和初始化复活对象,但是通常还是用Interface Builder来进行这些工作。
幕后方式。在程序运行时,Cocoa会在“幕后”为其创建一些框架对象。您不需要显式分配和初始化这些对象,框架会自动替您完成这些工作。这些类通常是私有的,但是要实现期望的行为。
一般类的方式。框架中有一些一般类,或者说抽象类。框架可能提供一些一般类的具体子类,不经修改就可以使用。然而您可以—在某些情况下是必须—定义您自己的子类,并重载特定的方法。NSView、NSDocument、和NSFormatter类就是这种类的例子。
委托者和通告者方式。很多框架对象将自己的动作通知给其它对象,甚至将特定的责任委托给其它对象。传递这种信息的机制就是委托和通告机制。委托者对象需要公布一个被称为非正式协议的接口,客户对象则必须首先注册为委托,然后实现该接口中的一个或多个方法。发布通告的对象要公布自己广播的通告列表,而所有的客户对象都可以自由监听其中的一个或多个公告。NSApplication、NSText、和NSWindow就是一些委托者类,而很多框架类都可以广播通告。
某些类提供不止一种类型的服务。举例来说,您可以将一个准备好的NSWindow对象从Interface Builder的选盘中拖出,并在少量的初始化之后进行使用。这样,NSWindow类就为您提供了复活实例。但是一个NSWindow对象也需要向它的委托发送消息,以及向其它对象发布通告。如果需要,比如您希望有一个圆角的窗口,您甚至可以生成NSWindow的子类。
采用最后两种服务方式—即一般类方式和委托者/通告者方式—的Cocoa类为将程序的具体代码集成到框架提供的结构中提供最大的可能性。"Cocoa类的继承"部分就如何创建框架类、特别是一般类的子类进行一般性的讨论,有关委托、通告、以及程序网络中的其它对象间通讯机制的信息请参见“和对象进行通讯”部分。
Cocoa API的约定
在您开始使用Cocoa框架中的类、方法、和其它API的时候,需要知道一些约定,它们都是为了确保使用效率和一致性而制订的。
返回对象的方法通常通过返回nil来表示“创建失败”或者“没有对象可以返回”。这种方法并不返回状态码。
返回nil的约定通常用于表示运行时错误或其它非例外的条件。Cocoa框架通过抛出例外(由最顶级的例外处理代码处理)来处理诸如“数组索引越界” 或 “不能识别方法选择器”这样的错误,如果方法签名有要求的话,同时返回nil。
某些可能返回nil的方法可以通过最后一个参数以引用的方式返回错误信息。
上述的最后一个参数是一个NSError对象的指针;对于执行失败的方法(也就是说方法返回nil),您可以通过考察返回的错误对象来确定错误的原因,或者将错误显示在对话框上。
以NSDocument类的一个方法为例:
- (id)initWithType:(NSString *)typeName error:(NSError **)outError;
类似地,执行某些系统操作的方法(比如文件读写)通常返回一个Boolean值,以指示执行成功还是失败。
这些方法也会将一个NSError对象指针作为最后一个引用参数。以NSData类的一个方法为例:
- (BOOL)writeToFile:(NSString *)path options:(unsigned)writeOptionsMask error:(NSError **)errorPtr;
Cocoa用空的容器对象来表示缺省值或没有值—nil通常不是正当的对象参数。
很多对象封装了对象的值或集合。将这些对象作为参数的方法可以接收表示没有值或缺省值的“空”对象(比如@"")。比如,下面的消息通过指定一个空的字符串,将一个窗口的关联文件名设置为“没有值”:
[aWindow setRepresentedFilename:@""];
请注意:Objective-C的@“characters”构造器用于创建一个包含文字字符的NSString对象,因此@““会创建一个不包含字符的字符串对象—或者说是一个空字符串。
Cocoa框架要求在字典键、通告和例外名称、以及一些用字符串作为参数的方法上使用全局字符串常数,而不是一个字符串文字。在可以进行选择的时候,您应该总是选择字符串常量,而不是字符串文字。使用字符串常量时,编译器可以帮助您进行拼写检查,这样可以避免运行时错误。Cocoa框架在类型使用上是一致的,各组API之间可以进行较好的匹配。
举例来说,Cocoa框架用float来表示坐标的值,用CGFloat类型表示图形和坐标值,用NSPoint(由两个CGFloat值组成)来表示坐标系统中的一个位置,用NSString对象来表示字符串的值,用NSRange来表示范围(起始点和偏移量),分别用NSInteger和NSUInteger来表示有符号和无符号的整数值。当您设计自己的API时,应该尽量保持类似的一致性。相当一部分Cocoa API约定关注于类、方法、函数、常量、和其它符号的命名。在您开始设计自己的编程接口时,需要知道这些约定。一部分较为重要的命名约定如下所示:
在类名和与类相关联的符号,比如函数和typedef定义的类型上,使用前缀。使用前缀可以避免命名冲突,帮助区分不同的函数域。前缀的命名约定是使用两个或三个唯一的大写字母,比如ACCircle中的“AC”。在API的命名上,清楚比简洁更重要。举例来说,removeObjectAtIndex:的功能很容易理解,但是remove:就有点模糊。避免模菱两可的命名。
比如displayName就模菱两可,因为我们不清楚它的功能是显示一个名称还是返回一个显示名称。在代表动作的函数或方法名上使用动词。如果一个方法返回一个属性或经过计算的值,则直接使用属性名作为方法名。这些方法就是所谓的“getter”存取方法。举例来说,如果属性是背景颜色,则getter方法应该命名为backgroundColor。返回Boolean值的getter方法的命名有细微的区别,采用“is”或“has” 前缀—比如hasColor。
对于负责设置属性值的方法(也就是“setter” 存取方法),则其名称以“set”开头,后接属性名称。属性名称的第一个字母是大写字母—比如setBackgroundColor:。请注意:有关如何实现setter和getter方法的详细讨论,请参见模型对象实现指南文档中的“存取方法”部分。不要在API名称上使用缩写,如果不是众所周知的缩写的话(比如HTML或TIFF)。