Objective-C 2.0 with Cocoa Foundation--- Class类型,选择器Selector以及函数指针(2)

来源:互联网 发布:淘宝客服催单技巧 编辑:程序博客网 时间:2024/05/05 00:45

  5.3,BOOL类型

  我们现在打开“DoProxy.h”文件。“DoProxy.h”文件的第3行到第5行是三个预定义的三个字符串的宏。我们将在程序当中使用这3个宏,为了实现代码的独立性,在实际的程序开发当中,我们也许考虑使用一个配置的文本文件或者一个XML来替代这些宏。但是现在由于笔者的主要目的是讲解objecsive-C的概念,为了避免较多的代码给大家带来理解主题的困难,所以笔者没有使用配置文件或者XML来表述这些可以设定的常量。

  “DoProxy.h”的第7行对同学们来说也是老朋友了,是通知编译器,我们需要声明一个DoProxy类,从NSobjecs继承。

 我们在第8行遇到了我们的一个新的朋友,BOOL:

    BOOL notFirstRun;

  我们定义了一个notFirstRun的实例变量,这个变量是布尔类型的。我们的实例方法doWithCattleId需要被执行多次,我们在第一次执行doWithCattleId的时候需要向控制输出包含doWithCattleId的方法名字的字符串,关于这个字符串的内容,请参考图5-1。

  好的,我们现在需要看看在objecsive-C里面BOOL是怎样定义的,我们把鼠标移动到BOOL上面,然后单击鼠标右键选择弹出菜单的“Jump to Definition”,然后Xcode会打开objc.h文件,我们看到下面的代码:

typedef signed char             BOOL;
// BOOL is explicitly signed so @encode(BOOL) == "c" rather than "C"
// even if -funsigned-char is used.
#define OBJC_BOOL_DEFINED


#define YES             (BOOL)1
#define NO              (BOOL)0

  我们看到这段代码,我们可以这样理解,在objecsive-C里面,BOOL其实是signed char,YES是1,NO是0。我们可以这样给BOOL赋值:

BOOL x = YES;
BOOL y = NO;

  关于BOOL,实际上就是一个开关的变量,但是我们需要注意下面2点:

  第一点,从本质上来说BOOL是一个8bit的一个char,所以我们在把其他比如说short或者int转换成为BOOL的时候一定要注意。如果short或者int的最低的8位bit都是0的话,尽管除了最低的8位以外都不是0,那么经过转换之后,就变成了0也就是NO。比如说我们有一个int的值是0X1000,经过BOOL转换之后就变成了NO。

if(0X1000)
if(2)
if(-1)

  5.4,SEL类型

  让我们接着看“DoProxy.h”文件的下列代码:

1     id cattle[3];
2     SEL say;
3     SEL skin;

  其中id cattle[3]定义了一个数组用于存储Cattle或者Bull对象。这一行代码估计大家都很熟悉,笔者就不赘述了。像这样的传统的数组并不能完全满足我们的需求,当我们需要做诸如追加,删除等操作的时候,会很不方便。在随后的章节里面笔者将要向大家介绍传统数组的替代解决方案NSArray。

  上一段代码的第二行和第三行是本节所关注的,就是SEL类型。objecsive-C在编译的时候,会根据方法的名字(包括参数序列),生成一个用 来区分这个方法的唯一的一个ID,这个ID就是SEL类型的。我们需要注意的是,只要方法的名字(包括参数序列)相同,那么它们的ID都是相同的。就是 说,不管是超类还是子类,不管是有没有超类和子类的关系,只要名字相同那么ID就是一样的。除了函数名字和ID,编译器当然还要把方法编译成为机器可以执 行的代码,这样,在一个编译好的类里面,就产生了如下图所示方法的表格示意图(本构造属于笔者推测,没有得到官方证实,所以图5-2为示意图仅供参考,我们可以暂时认为是这样的)。

objecsive-C 2.0 with Cocoa Foundation--- 5,Class类型,选择器Selector以及函数指针 

 图5-2,方法的表格示意图

  请注意setSkinColor后面有一个冒号,因为它是带参数的。由于存在这样的一个表格,所以在程序执行的时候,我们可以方便的通过方法的名字,获取到方法的ID也就是我们所说的SEL,反之亦然。具体的使用方法如下:

1     SEL 变量名 = @selector(方法名字);
2     SEL 变量名 = NSSelectorFromString(方法名字的字符串);
3     NSString *变量名 = NSStringFromSelector(SEL参数);

  其中第1行是直接在程序里面写上方法的名字,第2行是写上方法名字的字符串,第3行是通过SEL变量获得方法的名字。我们得到了SEL变量之后,可以通过下面的调用来给一个对象发送消息:

  [对象 performSelector:SEL变量 withobjecs:参数1 withobjecs:参数2];

  这样的机制大大的增加了我们的程序的灵活性,我们可以通过给一个方法传递SEL参数,让这个方法动态的执行某一个方法;我们也可以通过配置文件指定需要执行的方法,程序读取配置文件之后把方法的字符串翻译成为SEL变量然后给相应的对象发送这个消息。

  从效率的角度上来说,执行的时候不是通过方法名字而是方法ID也就是一个整数来查找方法,由于整数的查找和匹配比字符串要快得多,所以这样可以在某种程度上提高执行的效率。

  5.5,函数指针

  在讲解函数指针之前,我们先参看一下图5-2,函数指针的数值实际上就是图5-2里面的地址,有人把这个地址成为函数的入口地址。在图5-2里面我们可以通过方法名字取得方法的ID,同样我们也可以通过方法ID也就是SEL取得函数指针,从而在程序里面直接获得方法的执行地址。或者函数指针的方法有2种,第一种是传统的C语言方式,请参看“DoProxy.h” 的下列代码片断:

1     void(*setSkinColor_Func) (id, SEL, NSString*);
2     IMP say_Func;

  其中第1行我们定义了一个C语言里面的函数指针,关于C语言里面的函数指针的定义以及使用方法,请参考C语言的书籍和参考资料。在第一行当中,值得我们注意的是这个函数指针的参数序列:

  第一个参数是id类型的,就是消息的接受对象,在执行的时候这个id实际上就是self,因为我们将要向某个对象发送消息。

  第二个参数是SEL,也是方法的ID。有的时候在消息发送的时候,我们需要使用用_cmd来获取方法自己的SEL,也就是说,方法的定义体里面,我们可以通过访问_cmd得到这个方法自己的SEL。

  第三个参数是NSString*类型的,我们用它来传递skin color。在objecsive-C的函数指针里面,只有第一个id和第二个SEL是必需的,后面的参数有还是没有,如果有那么有多少个要取决于方法的声明。

  现在我们来介绍一下objecsive-C里面取得函数指针的新的定义方法,IMP。

  上面的代码的第一行比较复杂,令人难以理解,objecsive-C为我们定义了一个新的数据类型就是在上面第二行代码里面出现的IMP。我们把鼠标移动到IMP上,单击右键之后就可以看到IMP的定义,IMP的定义如下:

typedef id                      (*IMP)(id, SEL, objecsive-C 2.0 with Cocoa Foundation--- 5,Class类型,选择器Selector以及函数指针);

  这个格式正好和我们在第一行代码里面的函数指针的定义是一样的。

  我们取得了函数指针之后,也就意味着我们取得了执行的时候的这段方法的代码的入口,这样我们就可以像普通的C语言函数调用一样使用这个函数指针。当然我们可以把函数指针作为参数传递到其他的方法,或者实例变量里面,从而获得极大的动态性。我们获得了动态性,但是付出的代价就是编译器不知道我们要执行哪一个方法所以在编译的时候不会替我们找出错误,我们只有执行的时候才知道,我们写的函数指针是否是正确的。所以,在使用函数指针的时候要非常准确地把握能够出现的所有可能,并且做出预防。尤其是当你在写一个供他人调用的接口API的时候,这一点非常重要。

 5.6,Class类型

  到目前为止,我们已经知道了对应于方法的SEL数据类型,和SEL同样在objecsive-C里面我们不仅仅可以使用对应于方法的SEL,对于类在objecsive-C也为我们准备了类似的机制,Class类型。当一个类被正确的编译过后,在这个编译成功的类里面,存在一个变量用于保存这个类的信息。我们可以通过一个普通的字符串取得 这个Class,也可以通过我们生成的对象取得这个Class。Class被成功取得之后,我们可以把这个Class当作一个已经定义好的类来使用它。这样的机制允许我们在程序执行的过程当中,可以Class来得到对象的类,也可以在程序执行的阶段动态的生成一个在编译阶段无法确定的一个对象。

  因为Class里面保存了一个类的所有信息,当然,我们也可以取得一个类的超类。关于Class类型,具体的使用格式如下:

1     Class 变量名 = [类或者对象 class];
2     Class 变量名 = [类或者对象 superclass];
3     Class 变量名 = NSClassFromString(方法名字的字符串);
4     NSString *变量名 = NSStringFromClass(Class参数);

  第一行代码,是通过向一个类或者对象发送class消息来获得这个类或者对象的Class变量。

  第二行代码,是通过向一个类或者对象发送superclass消息来获得这个类或者对象的超类的Class变量。

  第三行代码,是通过调用NSClassFromString函数,并且把一个字符串作为参数来取得Class变量。这个在我们使用配置文件决定执行的时候的类的时候,NSClassFromString给我们带来了极大的方便。

  第四行代码,是NSClassFromString的反向函数NSStringFromClass,通过一个Class类型作为变量取得一个类的名字。


原创粉丝点击