RTTI (Run-Time Type Information)
RTTI 即运行时类型信息,能够在运行的时候知道需要的类型信息。C++ 有时被认为是一个“假的”面向对象语言。相比 Objective-C,C++ 显得非常静态。这有利于在运行时获得最好的性能。C++ 使用 typeinfo 库提供运行时信息,但这不是安全的,因为这个库依赖于编译器的实现。一般来说,查找对象的类型是一个很少见的请求,因为语言是强类型的,一般在编译时就已经确定其类型了;但是,有时候这种能力对于容器很常用。我们可以使用dynamic_cast
和typeid
运算符,但是程序交互则会在一定程度上受限。那么,如何由名字获知这个对象的类型呢?Objective-C 语言可以很容易地实现这种操作。类也是对象,它们继承它们的行为。
class
,superclass
,isMemberOfClass
,isKindOfClass
对象在运行时获取其类型的能力称为内省。内省可以有多种方法实现。
isMemberOfClass:
可以用于回答这种问题:“我是给定类(不包括子类)的实例吗?”,而isKindOfClass:
则是“我是给定类或其子类的实例吗?”使用这种方法需要一个“假”关键字的class
(注意,不是@class
,@class
是用于前向声明的)。事实上,class
是NSObject
的一个方法,返回一个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 的函数。这让我们可以很方便的在运行时修改类。