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

来源:互联网 发布:网络金融销售 编辑:程序博客网 时间:2024/04/30 14:13

 上一章笔者介绍了在objecsive-C里面继承的概念。有了继承的知识我们可以重复的使用很多以前生效的代码,这样就大大的提高了代码开发的效率。在本章,笔者要向同学们介绍几个非常重要的概念,Class类型, 选择器Selector以及指针函数。

  我们在实际上的编程过程中,也许会遇到这样的场景,那就是我们在写程序的时候不能确切的知道我们需要使用什么类,使用这个类的什么方法。在这个时候,我们需要在我们的程序里面动态的根据用户的输入来创建我们在写程序不知道的类的对象,并且调用这个对象的实例方法。objecsive-C为我们提供了Class类型, 选择器Selector以及指针函数来实现这样的需求,从而大大的提高了我们程序的动态性能。

  在objecsive-C里面,一个类被正确的编译过后,在这个编译成功的类里面,存在一个变量用于保存这个类的信息。我们可以通过一个普通的字符串取得这个Class,也可以通过我们生成的对象取得这个Class。Class被成功取得之后,我们可以把这个Class当作一个已经定义好的类来使用它。

  Selector和Class比较类似,不同的地方是Selector用于表示方法。 在objecsive-C的程序进行编译的时候,会根据方法的名字(包括参数列表)确定一个唯一的身份证明(实际上就是一个整数),不用的类里面的相同名字相同声明的方法的身份证明是一样的。这样在程序执行的时候,runtime就不用费力的进行方法的名字比较来确定是执行哪一个方法了,只是通过一个整数的寻找就可以马上定位到相应的方法,然后找到相应的方法的入口地址,这样方法就可以被执行了。

  笔者在前面的章节里面叙述过,在objecsive-C里面消息也就是方法的执行比C语言的直接找到函数入口地址执行的方式,从效率上来讲是比较低下的。尽管objecsive-C使用了Selector等招数来提高寻找效率,但是无论如何寻找的过程,都是要消耗一定的时间的。好在objecsive-C是完全兼容C的,它也有指针函数的概念。当我们需要执行效率的时候,比如说在一个很大的循环当中需要执行某个功能的时候,我们可以放弃向对某一个对象发送消息的手段,用指针函数取而代之,这样就可以获得和C语言一样的执行效率了。

 说到这里,可能有的同学已经有些茫然了。这些概念有些令人难以理解,但是它们确实是objecsive-C的核心的功能。掌握了这些核心的功能之后,同学们可以很轻松的看懂苹果的SDK里面的很多东西含义,甚至可以自己动手写一些苹果没有为我们提供的功能。所以建议大家仔细研读本章的内容,如果有什么问题,可以发个帖子大家可以共同探讨。

  从笔者的观点上来看,对于有Java或者C++或者其他面向对象的语言的经验的同学来说,前面的从第1到第4章的内容也许有些平淡无奇。从第5章开始,我们将要逐渐的深入到objecsive-C的核心部分。笔者的最终目的,虽然是向大家介绍iphone开发的入门,但是笔者认为了解了objecsive-C的基本概念以及使用方法之后,熟悉iPhone的应用程序的开发将是一件水到渠成的轻松的事情。否则如果你直接就深入到iPhone的开发的话,在绝大多数时间你也许因为一个小小的问题就会困扰你几个小时甚至几天,解决这些问题的唯一方法就是熟悉objecsive-C和Cocoa Foundation的特性。

  好了,说了很多我们从下面就要开始,我们的手法和前面几章是一样的,我们首先要介绍一下本章程序的执行结果。

  5.1,本章程序的执行结果

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

 

  图5-1,第5章程序的执行结果

  在本章里面,我们将要继续使用我们在前面几章已经构筑好的类Cattle和Bull。为了灵活的使用Cattle和Bull,我们将要构筑一个新的类,DoProxy。在DoProxy里面,我们将会引入几个我们的新朋友,他们分别是BOOL,SEL,IMP,CLASS。通过这些新的朋友我们可以动态的通过设定文件取得Cattle和Bull的类,还有方法以及方法指针。下面将要介绍如何构筑本章程序。同学们可以按照本章所述的步骤来构筑,也可以通过下载。不过为了熟悉代码的写作,笔者强烈建议大家按照笔者所述的步骤来操作。

5.2,实现步骤

  第一步,按照我们在第2章所述的方法,新建一个项目,项目的名字叫做05-Hello Selector。如果你是第一次看本篇文章,请到这里参看第二章的内容。

  第二步,按照我们在第4章的4.2节的第二,三,四步所述的方法,把在第4章已经使用过的“Cattle.h”,“Cattle.m”,“Bull.h”还有“Bull.m” 导入本章的项目里面。

  第三步,把鼠标移动到项目浏览器上面的“Source”上面,然后在弹出的菜单上面选择“Add”,然后在子菜单里面选择“New Files”,然后在新建文件对话框的左侧选择“Cocoa Touch Classes”,然后在右侧窗口选择“NSobjecs subclass”,选择“Next”,在“New File”对话框里面的“File Name”栏内输入“DoProxy.m”。在这里笔者没有给出图例,在这里新建文件的步骤和第3章的第二步到第四步相同,只是文件名字不一样。第一次看到本篇 文章的同学可以参照第3章。

  第四步,打开“DoProxy.h”做出如下修改并且保存

#import 

#define SET_SKIN_COLOR @"setSkinColor:"
#define BULL_CLASS @"Bull"
#define CATTLE_CLASS @"Cattle"

@interface DoProxy : NSobjecs {
     BOOL notFirstRun;
     id cattle[3];
     SEL say;
     SEL skin;
     void(*setSkinColor_Func) (id, SEL, NSString*);
     IMP say_Func;
     Class bullClass;
}
- (void) doWithCattleId:(id) aCattle colorparem:(NSString*) color;
- (void) setAllIVars;
- (void) SELFuncs;
- (void) functionPointers;
@end

 第五步,打开“DoProxy.m”做出如下修改并且保存

#import "DoProxy.h"
#import "Cattle.h"
#import "Bull.h"

@implementation DoProxy
- (void) setAllIVars
{
     cattle[0] = [Cattle new];
    
     bullClass = NSClassFromString(BULL_CLASS);
     cattle[1] = [bullClass new];
     cattle[2] = [bullClass new];
    
     say = @selector(saySomething);
     skin = NSSelectorFromString(SET_SKIN_COLOR);
}
- (void) SELFuncs
{
     [self doWithCattleId:cattle[0] colorparem:@"brown"];
     [self doWithCattleId:cattle[1] colorparem:@"red"];
     [self doWithCattleId:cattle[2] colorparem:@"black"];
     [self doWithCattleId:self colorparem:@"haha"];
}
- (void) functionPointers
{
     setSkinColor_Func=(void (*)(id, SEL, NSString*)) [cattle[1] methodForSelector:skin];
     //IMP setSkinColor_Func = [cattle[1] methodForSelector:skin];
     say_Func = [cattle[1] methodForSelector:say];
     setSkinColor_Func(cattle[1],skin,@"verbose");
     NSLog(@"Running as a function pointer will be more efficiency!");
     say_Func(cattle[1],say); 
}
- (void) doWithCattleId:(id) aCattle colorparem:(NSString*) color
{
     if(notFirstRun == NO)
     {
         NSString *myName = NSStringFromSelector(_cmd);
         NSLog(@"Running in the method of %@", myName);
         notFirstRun = YES;
     }
    
     NSString *cattleparemClassName = [aCattle className];
     if([cattleparemClassName isEqualToString:BULL_CLASS] || 
        [cattleparemClassName isEqualToString:CATTLE_CLASS])
     {
         [aCattle setLegsCount:4];
         if([aCattle respondsToSelector:skin])
         {
             [aCattle performSelector:skin withobjecs:color];
         }
         else
         {
             NSLog(@"Hi, I am a %@, have not setSkinColor!", cattleparemClassName);
         }
         [aCattle performSelector:say];
     }
     else
     {
         NSString *yourClassName = [aCattle className];
         NSLog(@"Hi, you are a %@, but I like cattle or bull!", yourClassName);
     }
}
@end

 第六步,打开“05-Hello Selector.m” 作出如下修改并且保存

#import 
#import "DoProxy.h"

int main (int argc, const char * argv[]) {
     NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
     DoProxy *doProxy = [DoProxy new];
    
     [doProxy setAllIVars];
     [doProxy SELFuncs];
     [doProxy functionPointers];
    
     [pool drain];
     return 0;
}

  第七步,选择屏幕上方菜单里面的“Run”,然后选择“Console”,打开了Console对话框之后,选择对话框上部中央的“Build and Go”,如果不出什么意外的话,那么应该出现入图5-1所示的结果。如果出现了什么意外导致错误的话,那么请仔细检查一下你的代码。


原创粉丝点击