当我们在程序里面通过使用上面的第一,二或者第三行代码成功的取得一个Class类型的变量,比如说我们把这个变量名字命名为myClass,那么我们在以后的代码种可以把myClass当作一个我们已经定义好的类来使用,当然我们可以把这个变量作为参数传递到其他的方法当中让其他的方法动态的生成我们需要的对象。
5.7,DoProxy.h里面的方法定义
DoProxy.h里面还有一些实例方法,关于方法的定义的格式,同学们可以参照第三章。我们现在要对DoProxy.h里面定义的方法的做一下简要的说明。
1 - (void) doWithCattleId:(id) aCattle colorparem:(NSString*) color;
2 - (void) setAllIVars;
3 - (void) SELFuncs;
4 - (void) functionPointers;
第一行的方法,是设定aCattle,也就是Cattle或者Bull对象的属性,然后调用saySomething方法,实现控制台的打印输出。
第二行的方法,是把我们定义的DoProxy类里面的一些变量进行赋值。
第三行的方法,是调用doWithCattleId方法。
第四行的方法,是调用了函数指针的方法。
好的,我们把DoProxy.h的内容介绍完了,让我们打开DoProxy.m。
5.8,DoProxy.m的代码说明
有了DoProxy.h的说明,同学们理解DoProxy.m将是一件非常轻松的事情,让我们坚持一下把这个轻松的事情搞定。由于篇幅所限,笔者在这里的讲解将会省略掉非本章的内容。
DoProxy.m代码如下:
1 #import "DoProxy.h"
2 #import "Cattle.h"
3 #import "Bull.h"
4
5 @implementation DoProxy
6 - (void) setAllIVars
7 {
8 cattle[0] = [Cattle new];
9
10 bullClass = NSClassFromString(BULL_CLASS);
11 cattle[1] = [bullClass new];
12 cattle[2] = [bullClass new];
13
14 say = @selector(saySomething);
15 skin = NSSelectorFromString(SET_SKIN_COLOR);
16 }
17 - (void) SELFuncs
18 {
19 [self doWithCattleId:cattle[0] colorparem:@"brown"];
20 [self doWithCattleId:cattle[1] colorparem:@"red"];
21 [self doWithCattleId:cattle[2] colorparem:@"black"];
22 [self doWithCattleId:self colorparem:@"haha"];
23 }
24 - (void) functionPointers
25 {
26 setSkinColor_Func=(void (*)(id, SEL, NSString*)) [cattle[1] methodForSelector:skin];
27 //IMP setSkinColor_Func = [cattle[1] methodForSelector:skin];
28 say_Func = [cattle[1] methodForSelector:say];
29 setSkinColor_Func(cattle[1],skin,@"verbose");
30 NSLog(@"Running as a function pointer will be more efficiency!");
31 say_Func(cattle[1],say);
32 }
33 - (void) doWithCattleId:(id) aCattle colorparem:(NSString*) color
34 {
35 if(notFirstRun == NO)
36 {
37 NSString *myName = NSStringFromSelector(_cmd);
38 NSLog(@"Running in the method of %@", myName);
39 notFirstRun = YES;
40 }
41
42 NSString *cattleparemClassName = [aCattle className];
43 if([cattleparemClassName isEqualToString:BULL_CLASS] ||
44 [cattleparemClassName isEqualToString:CATTLE_CLASS])
45 {
46 [aCattle setLegsCount:4];
47 if([aCattle respondsToSelector:skin])
48 {
49 [aCattle performSelector:skin withobjecs:color];
50 }
51 else
52 {
53 NSLog(@"Hi, I am a %@, have not setSkinColor!", cattleparemClassName);
54 }
55 [aCattle performSelector:say];
56 }
57 else
58 {
59 NSString *yourClassName = [aCattle className];
60 NSLog(@"Hi, you are a %@, but I like cattle or bull!", yourClassName);
61 }
62 }
63 @end
第10行代码是通过一个预定义的宏BULL_CLASS取得Bull的Class变量。
第11和12行代码是使用bullClass来初始化我们的cattle实例变量数组的第2和第3个元素。
第14行是通过@selector函数来取得saySomething的SEL变量。
第15行是通过向NSSelectorFromString传递预定义的宏SET_SKIN_COLOR来取得setSkinColor的SEL变量。
第22行,笔者打算“戏弄”一下doWithCattleId,向传递了不合适的参数。
第26行,笔者取得了传统的C语言的函数指针,也是使用了第5.5节所述的第一种取得的方法。
第28行,笔者通过5.5节所述的第二种取得的方法得到了函数指针say_Func。
第29行和31行分别执行了分别在第26行和28行取得的函数指针。
第35行是一个BOOL型的实例变量notFirstRun 。当对象被初始化之后,确省的值是NO。第一次执行完毕之后,我们把这个变量设定成为YES,这样就保证了花括号里面的代码只被执行一次。
第37行我们通过_cmd取得了doWithCattleId这个方法名字用于输出。当然同学们在设计方法的提供给别人使用的时候,为了防止使用方法的人把这个方法本身传递进来造成死循环,需要使用_cmd这个系统隐藏的变量判断一下。笔者在这里没有做出判断,这样写从理论上来说存在一定的风险。
第42行,我们通过向对象发送className消息来取得这个对象的类的名字。
第43行和第44行,我们通过NSString的方法isEqualToString来判断取得的类的名字是否在我们事先想象的范围之内,我们只希望接受Bull或者Cattle类的对象。
第46行,本来我们想通过SEL的方式来进行这个牛股的设定,但是由于它的参数不是从NSobjecs继承下来的,所以我们无法使用。我们会有办法解决这个问题的,我们将在后面的章节里面介绍解决这个问题的方法。
第59行,我们通过类的名字发现了一个假冒的Cattle,我们把这个假冒的家伙给揪出来,然后实现了屏幕打印。
5.9,本章总结
本章给同学们介绍了几个新的数据类型,以及使用方法,这些数据类型分别是BOOL,SEL,Class,IMP。