解析Objective-C反射
Objective-C反射是本文要介绍的内容,我第一次接触Java的时候就觉得整个反射包都很新颖,它使得Java和解释型的脚本语言更接近了,与此同时也拉开了和主流的C和C++的距离。
在运行时可以窥视到一个对象的类元数据真的很不可思议,尽管这些可能不会在日常应用编程中经常使用到。从Java转到Objective-C的程序员应该会乐见Objective-C也支持反射。实际上,Objective-C有很多诸如动态改变类定义以及创建一个新类的动态特征。
不过很难说这些功能有多大的作用,这也让我觉得Objective-C是一个有些臃肿的语言。在我看来,Objective-C有一个定位危机:它是解释型语言还是编译语言?运行时很大程度上是动态的。和C++不同,Objective-C是运行时绑定的。这也是为什么我们可以在实现时定义一个从来没有在头文件中声明的方法,或者通过Category扩展类。不幸的是由于这种臃肿使得找到一些日常编程中有用的东西变得困难,本文就是要去发现其中的一些“宝藏”。
根类NSObject
大部分(如果不是全部的话)的动态反射支持来自NSObject 类。和Java的Object对象类似,NSObject是所有类(除了一些很少见的例外)的根类。所以所有你写的类应该都可以支持反射。需要指出的所有这些的反射支持并不是Objective-C语言的一部分,而是源于NS*的运行时环境。这也是为什么这些东西感觉被加入一些额外东东的原因。因为它就是被加入了额外东东。
获取类的元数据 通过调用如下的类方法你就可以获取到一个对象的类的元数据:
Class c = [self class];
该方法既是实例方法也是类方法。它返回一个带有很多神奇信息的C构造体,比如实例变量、方法等等。所有这些和java.lang.reflect包相比都有些过时了,利用Objective-C访问这些信息的接口看起来很复杂。这可能就是故意设计成这样来“过滤”一些不合格的程序员。目前为止我唯一使用这些的地方就是为下面将要介绍的isKindOfClass:方法提供参数。一直以来我都不需要去窥视类结构的内容。
动态方程调用我已经在方法调用一文中介绍了反射的一个方面。这使得你可以在运行时创建一个方法调用并传入参数。这和Java中使用java.lang.reflect.Method类很相似。
检查继承关系
Java有一个名为instanceof的操作符可以用来检查一个对象是否是一个特定类或者接口的实例。 Objective-C也有类似的功能,就是通过isKindOfClass:方法。isKindOfClass:会在消息接收者是指定类及其子类的实例的情况下返回YES。比如有一个关联的指针数组,这样就可以根据其类型进行不同的操作:
for(BaseClass* base in myArray) { if([base isKindOfClass:[ClassOne class]]) { // do stuff specific to ClassOne } else if([base isKindOfClass:[ClassTwo class]]) { // do stuff specific to ClassTwo } else if([base isKindOfClass:[ClassThree class]]) { // do stuff specific to ClassThree } // etc }
如果你需要一个精确的类匹配,而不是匹配任何继承类,你就可以使用isMemberOfClass:方法。
检查是否符合协议 和实例检查类似,你可以测试一个对象是否符合特定的协议。Java在类和接口的情况下都使用instanceof 方法搞定,但Objective-C使用了一个更笨重的方法。在测试是否合规的时候,应该使用conformsToProtocol:方法:
BOOL conforms = [obj conformsToProtocol:@protocol(MyInterface)];
检查方法是否存在对于像我这样Java和C++的老手来说,如果不知道一个对象是否实现了一个方法就很奇怪了。但是Objective-C的类很大程度上是动态的,你就需要检查你需要的方法是否存在。这就需要respondsToSelector:方法。如下代码就是检查接收者是否实现(或者继承)了指定方法:
if([obj respondsToSelector:@selector(aMethod:)]) { // it's there, so we can call it [obj aMethod:YES]; }
当然,利用Objective-C的反射你可以做更多的事情,这里我只是尝试谈谈反射机制最常见的应用。如果你需要在你的软件中加入核心的动态特性,你就需要熟悉下这些文档: