从 C++ 到 Objective-C(23):动态

RTTI (Run-Time Type Information)

RTTI 即运行时类型信息,能够在运行的时候知道需要的类型信息。C++ 有时被认为是一个“假的”面向对象语言。相比 Objective-C,C++ 显得非常静态。这有利于在运行时获得最好的性能。C++ 使用 typeinfo 库提供运行时信息,但这不是安全的,因为这个库依赖于编译器的实现。一般来说,查找对象的类型是一个很少见的请求,因为语言是强类型的,一般在编译时就已经确定其类型了;但是,有时候这种能力对于容器很常用。我们可以使用dynamic_casttypeid运算符,但是程序交互则会在一定程度上受限。那么,如何由名字获知这个对象的类型呢?Objective-C 语言可以很容易地实现这种操作。类也是对象,它们继承它们的行为。

class,superclass,isMemberOfClass,isKindOfClass

对象在运行时获取其类型的能力称为内省。内省可以有多种方法实现。

isMemberOfClass:可以用于回答这种问题:“我是给定类(不包括子类)的实例吗?”,而isKindOfClass:则是“我是给定类或其子类的实例吗?”使用这种方法需要一个“假”关键字的class(注意,不是@class@class是用于前向声明的)。事实上,classNSObject的一个方法,返回一个Class对象。这个对象是元类的一个实例。请注意,nil值的类是Nil

BOOL test = [self isKindOfClass:[Foo class]];
if (test)
    printf("I am an instance of the Foo class\n");

注意,你可以使用 superclass 方法获取其父类。

conformsToProtocol:

该方法用于确定一个对象是否和某一协议兼容。我们前面曾经介绍过这个方法。它并不是动态的。编译器仅仅检查每一个显式声明,而不会检查每一个方法。如果一个对象实现了给定协议的所有方法,但并没有显式声明说它实现了该协议,程序运行是正常的,但是conformsToProtocol:会返回NO

respondsToSelector:,instancesRespondToSelector:

respondsToSelector:是一个实例方法,继承自NSObject。该方法用于检查一个对象是否实现了给定的方法。这里如要使用@selector。例如:

if ( [self respondsToSelector:@selector(work)] )
{
    printf("I am not lazy.\n");
    [self work];
}

如果要检查一个对象是否实现了给定的方法,而不检查继承的方法,可以使用类方法instancesRespondToSelector:。例如:

if ([[self class] instancesRespondToSelector:@selector(findWork)])
{
    printf("I can find a job without the help of my mother\n");
}

注意,respondsToSelector:不能用于仅仅使用了前向声明的类。

强类型和弱类型id

C++ 使用的是强类型:对象必须符合其类型,否则不能通过编译。在 Objective-C 中,这个限制就灵活得多了。如果一个对象与消息的目标对象不相容,编译器仅仅发出一个警告,而程序则继续运行。这个消息会被丢弃(引发一个异常),除非前面已经转发。如果这就是开发人员期望的,这个警告就是冗余的;在这种情形下,使用弱类型的id来替代其真实类型就可以消除警告。事实上,任何对象都是id类型的,并且可以处理任何消息。这种弱类型在使用代理的时候是必要的:代理对象不需要知道自己被使用了。例如:

-(void) setAssistant:(id)anObject
{
    [assistant autorelease];
    assistant = [anObject retain];
}
-(void) manageDocument:(Document*)document
{
    if ([assistant respondToSelector:@(manageDocument:)])
        [assistant manageDocument:document];
    else
        printf("Did you fill the blue form ?\n");
}

在 Cocoa 中,这种代理被大量用于图形用户界面的设计中。它可以很方便地把控制权由用户对象移交给工作对象。

运行时操作 Objective-C 类

通过添加头文件#import <objc/objc-runtime.h>,我们可以调用很多工具函数,用于在运行时获取类信息、添加方法或实例变量。Objective-C 2.0 又引入了一些新函数,比 Objective-C 1.0 更加灵活(例如使用class_addMethod(...)替代class_addMethods(...)),同时废弃了许多 1.0 的函数。这让我们可以很方便的在运行时修改类。

Leave a Reply