您的位置:首页 > 移动开发 > Objective-C

Objective-C反射机制

2015-07-26 14:36 246 查看
      Objective-C也提供了类似于java的反射机制,这种动态编程机制可以让Objective-C语言更加灵活。Objective-C提供了3种编程方式与运行环境交互。

     直接通过Objective-C的源代码。这是最常见的方式,开发人员只是编写Objective-C源代码,而运行环境负责在后台工作。当编译Objective-C源代码时,编译器会创建大量数据结构和动态机制的函数。其中数据结构负责保护类、类别(category)中定义的数据,而函数则负责处理方法调用。
通过NSObject类中定义的方法进行动态编程。因为绝大部分类都是NSObject的子类(NSProxy例外),所以绝大多部分对象都继承了NSobject的方法,故所有的对象都可以直接调用NSObject的方法来编程,在少数情况下,NSObject类只提供了方法模板,并没有为方法实现提供代码。例如,NSObject实现了description方法,该方法返回对象的类名+地址组成的字符串,通过该字符串拉来描述对象,而NSObject的子类则可重写该方法返回更多的详细信息。除此之外,NSObject还提供了isKindOfClass:、isMemberOfClass:方法,这两个方法可用于判断该对象所属的类;respondsToSelector:用于判断该实例是否可调用指定方法;conformsToProtocal:用于判断该对象是否遵守指定协议;methodForSelector:则用于返回指定方法的指针。
 直接调用运行时函数进行动态编程。运行时系统十一个动态共享库,由一系列位于/usr/include/objc目录的头文件中的函数和数据结构组成,这些工具都是C风格的,它们并不是Objective-C编程必需的,但有些函数在Objective-C编程是也是有用的。如果需要理解这些运行时函数的功能和用法,可参考Xcode帮助系统的Objective-C Runtime Reference帮助手册。

一、获得Class

    每个类都有一个对应的Class,在Objective-C程序中获得Class通常有如下3种方式。

使用Class NSClassFromString(NSString *aClassName)函数来获取Class,该函数需要传入字符串参数,该字符串参数的值是某个类的类名。

调用某个类的class方法来获取该类对应的Class。例如,[Person class]将会返回Person类对应的Class。

调用某个对象的class方法,该方法是NSObject类中的实例方法,所以所有的Objective-C对象都可以调用该方法,该方法将会返回该对象所属类对应的Class。

    对于定义中方式和第二种方式,都是直接根据类来取得该类的Class对象的,但相比之下,第二种方式有如下两种优势:

    1.代码更安全,程序在编译阶段就可以检查需要访问的Class对象是否存在。

    2.程序性能更好,因为这种方法无需调用方法。

    如果有可能,则应该使用第二种方式来获取指定类的Class。但在实际编程过程中,有时候只能拿到一个字符串,如@”Person”,如果需要获取该字符串对应的Class。则只能使用第一种方式。

    一旦获得了某个类所对应的Class对象之后,测很难过心虚就可以调用Class对象的方法来获得该对象和该类的真实信息了。

例如,如下程序来测试获取Class。

ClassTest.m
int main(int argc, char * argv[]){

@autoreleasepool{
//通过字符串获取Class
Class clazz = NSClassFromString(@”NSDate”);
NSLog(@”%@”,clazz);
//直接使用class来创建对象
id date = [[clazz alloc] init];
NSLog(@”%@”,date);
//通过对象来获取Class
NSLog(@”%@”,[date class]);
//通过类来获取Class
NSLog(@”%d”,clazzz == NSDate.class);
}

}
    上面程序测试通过3种方是来获取Class。变异、运行该程序,可以看到如下输出:

NSDate
2015-07-25 13:49:52 +0000
__NSDate
1

    从上面的与运行结果可以看出,通过类名字符串、类本身获取的Class对象是相等的,但通过NSDate对喜爱那个获取的Class则是__NSDate,这是什么原因呢?Objective-C的很多设计都采用类簇的设计,NSDate只是这个类簇的前端,当程序调用[[NSDate alloc] init];创建对象时,程序实际返回的只是NSDate的子类(__NSDate)的实例,而不是NSDate的实例。因此,程序直接调用date对象的class方法来获取Class时返回__NSDate。

    从上面的程序可以看出,一旦获得某个类对应的Class之后,程序可以直接调用该Class的alloc方法来创建对象,接下来调用init方法执行初始化即可。从程序的与西宁结果可以看到,调用Class的alloc方法创建的并不是Class的实例,而是该Class对应的类的。

二、检查继承关系

      如果程序只是需要确认一个类的继承关系,比如,判断它是否为某个类的实例或是否为某个类及其子类的实例,则可以直接带哦用NSObject提供的如下方法来进行判断。

isKindOfClass:该方法需要传入一个Class参数,用于判断该对象是否为该类及其子类的实例。

 isMemberOfClass:该方法需要传入一个Class参数,用于判断该对象是否为该类的实例。

conformsToProtocal:该方法需要传入一个Protocal参数,用于判断该对象是否实现了该协议。

      在上面的3个方法中,第三个方法需要传入一个Protocal参数,为了在程序中获取Protocal对象,可通过如下两种方式来完成。使用Objective-C提供的@protocal指令来实现。可调用Protocal *NSProtocalFromString(NSString *namestr);函数根据协议名字符串来获取对应的协议。

三、动态调用方法

      如果程序需要动态调用对象的setter、getter方法,则可通过Objective-C提供的KVC机制来实现;如果程序需要访问对象的实例变量的值,那么不管这个实例变量是否存在类的接口部分定义,也不管该变量使用哪种访问控制符修饰,或者是否在类的实现部分定义,程序都可以通过KVC机制来设置、访问实例变量的值。

      如果程序需要判断某个对象是否可以调用方法,则可通过NSObject的如下方法来进行判断。

      respondsToSelector::该方法需要传入一个SEL参数--Objective-C把方法称为选择器,因此,Objective-C使用SEL对象来代表方法。如果该对象可调用该方法,那么该方法返回YES:否则发返回NO。

      在上面的方法中传入一个方法作为参数,即需要传入SEL对象作为参数。为了在程序中动态获取SEL对象,Objective-C提供了如下方法。

 使用@selector指令来获取当前类中指定的方法。该指令需要传入完整的的方法签名关键字作为参数,仅有方法名是不够的。
使用SEL NSSelectorFromString(NSString *aSelectorName)函数根据方法签名关键字的字符串获取对应的方法。
      如果程序需要动态调用对象的普通方法,则可通过如下两种方式来实现。

 通过NSObject提供的系列performSelector:方法来实现,该方法的第一个西安书需要传入一个SEL对象。如果调用方法需要传入参数,则还可以通过withObject:标签来传入参数。
使用objc_msgSend(receiver,selector,...)函数来调用。该函数的第一个参数是方法调用者,第二个参数代表调用的方法,接下来的参数将作为调用方法的参数。

注:参考自《疯狂IOS讲义(上)》

 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息