9.5 有关类的问题

开始使用可以包含来自不同类的对象的变量时,可能会遇到以下问题:

·这个对象是矩形吗?

·这个对象支持print方法吗?

·这个对象是Graphics类或是其子类的成员吗?

这些问题的答案可以用来执行不同的代码序列,避免错误或在运行程序时检查程序的完整性。

表9-1总结了NSObject类所支持的一些基本方法,它们用来提出这类问题。在这个表中,class-object是一个类对象(通常是由class方法产生的),selector是一个SEL类型的值(通常是由@selector指令产生的)。

9.5 有关类的问题 - 图1

这里没有描述其他可用的方法,这些方法允许你提出关于是否符合某项协议的问题(协议将在第11章中讲述),还有一些方法允许你提出有关动态解析方法(本文中不讨论)的问题。

要根据类名或另一个对象生成一个类对象,可以向它发送class消息。所以,要从名为Square的类中获得类对象,可以编写如下代码:


[Square class]


如果mySquare是Square对象的实例,可以通过如下代码知道它所属的类:


[mySquare class]


要查看存储在变量obj1和obj2中的对象是不是相同的类实例,可以编写如下的代码:


if([obj1 class]==[obj2 class])

……


要查看变量myFract是不是Fraction类的实例,可如下测试表达式的结果:


[myFract isMemberOf:[Fraction class]]


要生成表9-1中列出的所谓的selector,可以对一个方法名应用@selector指令。例如,


@selector(alloc)


为名为alloc的方法生成一个SEL类型的值,你知道这个方法是从NSObject类继承的。表达式


@selector(setTo:over:)


为setTo:over:方法生成一个selector,这个setTo:over:方法是在Fraction类中实现的(切记方法名称中的冒号字符)。

要查看Fraction类的实例是否响应setTo:over方法,可以如下测试表达式的返回值:


[Fraction instancesRespondToSelector:@selector(setTo:over:)]


记住,测试包括继承的方法,并不是只测试直接定义在类中的方法。

performSelector:方法和它的变形版本(在表9-1中没有显示)允许你向对象发送消息,这条消息可以是存储在变量中的selector。例如,考虑以下代码序列:


SEL action;

id graphicObject;

……

action=@selector(draw);

……

[graphicObject performSelector:action];


在这个例子中,SEL变量action所指定的方法被发送到存储在grphicObject中的任何图形对象。根据推测,即使已经把这个行为指定为draw,在程序执行时,行为也可能发生变化,可能依赖于用户的输入。要先确定对象是否可以响应这个动作,可能使用以下方式:


if([graphicObject respondsToSelector:action]==YES)

[graphicObject perform:action]

else

//error handling code here


注意还可能由于重载doesNotRecognize:方法而捕获错误。只要向一个类发送一条未识别的消息并传递未识别的selector作为其参数,就会调用这个方法。

还可以采用其他策略:可以使用forward:方法将消息转发给其他人去处理,或者试着发送方法并捕获发生的异常。稍后将简要介绍这个技术。

代码清单9-3对在第8章“继承”中定义的Square和Rectangle类提出了一些问题。在查看实际输出之前,尝试预测程序的结果(现在可不能偷看!)。

代码清单9-3


import“Square.h”

int main(int argc, char*argv[])


{

NSAutoreleasePool*pool=[[NSAutoreleasePool alloc]init];

Square*mySquare=[[Square alloc]init];

//isMemberOf:

if([mySquare isMemberOfClass:[Square class]]==YES)

NSLog(@“mySquare is a member of Square class”);

if([mySquare isMemberOfClass:[Rectangle class]]==YES)

NSLog(@“mySquare is a member of Rectangle class”);

if([mySquare isMemberOfClass:[NSObject class]]==YES)

NSLog(@“mySquare is a member of NSObject class”);

//isKindOf:

if([mySquare isKindOfClass:[Square class]]==YES)

NSLog(@“mySquare is a kind of Square”);

if([mySquare isKindOfClass:[Rectangle class]]==YES)

NSLog(@“mySquare is a kind of Rectangle”);

if([mySquare isKindOfClass:[NSObject class]]==YES)

NSLog(@“mySquare is a kind of NSObject”);

//respondsTo:

if([mySquare respondsToSelector:@selector(setSide:)]==YES)

NSLog(@“mySquare responds to setSide:method”);

if([mySquare respondsToSelector:@selector(setWidth:andHeight:)]==YES)

NSLog(@“mySquare responds to setWidth:andHeight:method”);

if([Square respondsToSelector:@selector(alloc)]==YES)

NSLog(@“Square class responds to alloc method”);

//instancesRespondTo:

if([Rectangle instancesRespondToSelector:@selector(setSide:)]==YES)

NSLog(@“Instances of Rectangle respond to setSide:method”);

if([Square instancesRespondToSelector:@selector(setSide:)]==YES)

NSLog(@“Instances of Square respond to setSide:method”);

if([Square isSubclassOfClass:[Rectangle class]]==YES)

NSLog(@“Square is a subclass of a rectangle”);

[mySquare release];

[pool drain];

return 0;

}


一定要使用Square、Rectangle和XYPoint类(它们都出现在第8章“继承”中)的实现文件编译这个程序。

代码清单9-3输出


mySquare is a member of Square class

mySquare is a kind of Square

mySquare is a kind of Rectangle

mySquare is a kind of NSObject

mySquare responds to setSide:method

mySquare responds to setWidth:andHeight:method

Square class responds to alloc method

Instances of Square respond to setSide:method

Square is a subclass of a rectangle


代码清单9-3的输出应该很清晰。记住isMemberOf:测试类中的直接成员关系,而isKindOf:检测继承层次中的关系。所以mySquare是Square类的成员;但是它同样是“某种”Square、Rectangle和NSObject,因为它存在于这些类的层次结构中(很明显,所有对象都应该在有关NSObject类的isKindOf:的测试时返回YES,除非定义了新的根对象)。

测试语句


if([Square respondsTo:@selector(alloc)]==YES)


检测Square类是否响应alloc类方法,确实响应,因为Square类是从根对象NSObject继承来的。

现在你认识到总能在消息表达式中直接使用类名作为接收者,并且在以前的表达式中不必编写


[Square class]


不过如果你想编写,也可以编写。这是唯一可以这样做的地方。在其他地方就需要应用class的方法来获取类对象。