当前位置:  编程技术>移动开发
本页文章导读:
    ▪之后打死不用静态变量        以后打死不用静态变量 缺少经验、把静态变量当全局变量来用了、 activity结束掉之后这些变量在内存里还在、 没有被回收或者初始化产生一系列意料之外的问题、 全部改掉吧、时间不容.........
    ▪ Objective-C 2.0 with Cocoa Foundation- 五,Class类型,选择器Selector以及函数指针        Objective-C 2.0 with Cocoa Foundation--- 5,Class类型,选择器Selector以及函数指针 5,Class类型,选择器Selector以及指针函数 系列讲座有着很强的前后相关性,如果你是第一次阅读本篇文章,为了更好.........
    ▪ Java版A星算法兑现步骤       Java版A星算法实现步骤 A星算法步骤: 1.起点先添加到开启列表中。 2.开启列表中有节点的话,取出第一个节点,即最小F值的节点, 判断此节点是否是目标点,是则找到了,跳出, 根据此节.........

[1]之后打死不用静态变量
    来源: 互联网  发布时间: 2014-02-18
以后打死不用静态变量

缺少经验、把静态变量当全局变量来用了、

activity结束掉之后这些变量在内存里还在、

没有被回收或者初始化产生一系列意料之外的问题、

全部改掉吧、时间不容许、代价太大、不改吧、

程序实在是看不下去、骑虎难下啊这是、

吸取教训、还是多用私有变量、方法传值的方法、

哎、找本代码规范的书看看、


    
[2] Objective-C 2.0 with Cocoa Foundation- 五,Class类型,选择器Selector以及函数指针
    来源: 互联网  发布时间: 2014-02-18
Objective-C 2.0 with Cocoa Foundation--- 5,Class类型,选择器Selector以及函数指针
5,Class类型,选择器Selector以及指针函数

系列讲座有着很强的前后相关性,如果你是第一次阅读本篇文章,为了更好的理解本章内容,笔者建议你最好从本系列讲座的第1章开始阅读,请点击这里 。

 

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

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

 

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

 

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

 

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

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

 

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

 

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

 

5.1,本章程序的执行结果

 

 图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” 导入本章的项目里面。如果你没有第4章的代码,请到这里 下载。如果你没有阅读第4章的内容,请参看这里 。

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

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

 

#import <Foundation/Foundation.h>

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

@interface DoProxy : NSObject {
    BOOL notFirstRun;
    id cattle[3];
    SEL say;
    SEL skin;
    void(*setSkinColor_Func) (id, SEL, NSString*);
    IMP say_Func;
    Class bullClass;
}
- (void) doWithCattleId:(id) aCattle colorParam:(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] colorParam:@"brown"];
    [self doWithCattleId:cattle[1] colorParam:@"red"];
    [self doWithCattleId:cattle[2] colorParam:@"black"];
    [self doWithCattleId:self colorParam:@"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 colorParam:(NSString*) color
{
    if(notFirstRun == NO)
    {
        NSString *myName = NSStringFromSelector(_cmd);
        NSLog(@"Running in the method of %@", myName);
        notFirstRun = YES;
    }
    
    NSString *cattleParamClassName = [aCattle className];
    if([cattleParamClassName isEqualToString:BULL_CLASS] || 
       [cattleParamClassName isEqualToString:CATTLE_CLASS])
    {
        [aCattle setLegsCount:4];
        if([aCattle respondsToSelector:skin])
        {
            [aCattle performSelector:skin withObject:color];
        }
        else
        {
            NSLog(@"Hi, I am a %@, have not setSkinColor!", cattleParamClassName);
        }
        [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 <Foundation/Foundation.h>
#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所示的结果。如果出现了什么意外导致错误的话,那么请仔细检查一下你的代码。如果经过仔细检查发现 还是不能执行的话,可以到这里 下载笔者为同学们准备的代码。 如果笔者的代码还是不能执行的话,请告知笔者。

 

5.3,BOOL类型

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

 

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

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

 

    BOOL notFirstRun;

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

 

好的,我们现在需要看看在Objective-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
 

我们看到这段代码,我们可以这样理解,在Objective-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。

第二点,Objective-C里面的所有的逻辑判断例如if语句等等和C语言保持兼容,如果数值不是0判断为真,如果数值是0那么就判断为假,并不是说定义了BOOL值之后就变成了只有1或者YES为真。所以下面的代码的判断都为真:

 

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

5.4,SEL类型

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

 

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

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

 

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

 

 

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

 

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

 

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

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

 

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

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

 

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

 

5.5,函数指针

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

 

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

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

 

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

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

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

 

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

 

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

 

typedef id                      ( * IMP)(id, SEL,  );

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

 

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

 

5.6,Class类型

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

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

 

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

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

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

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

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

 

当我们在程序里面通过使用上面的第一,二或者第三行代码成功的取得一个Class类型的变量,比如说我们把这个变量名字命名为myClass,那么 我们在以后的代码种可以把myClass当作一个我们已经定义好的类来使用,当然我们可以把这个变量作为参数传递到其他的方法当中让其他的方法动态的生成 我们需要的对象。

 

5.7,DoProxy.h里面的方法定义

DoProxy.h里面还有一些实例方法,关于方法的定义的格式,同学们可以参照第三章。我们现在要对DoProxy.h里面定义的方法的做一下简要的说明。

 

- (void) doWithCattleId:(id) aCattle colorParam:(NSString*) color;
- (void) setAllIVars;
- (void) SELFuncs;
- (void) functionPointers;
 

第一行的方法,是设定aCattle,也就是Cattle或者Bull对象的属性,然后调用saySomething方法,实现控制台的打印输出。

第二行的方法,是把我们定义的DoProxy类里面的一些变量进行赋值。

第三行的方法,是调用doWithCattleId方法。

第四行的方法,是调用了函数指针的方法。

好的,我们把DoProxy.h的内容介绍完了,让我们打开DoProxy.m。

 

5.8,DoProxy.m的代码说明

有了DoProxy.h的说明,同学们理解DoProxy.m将是一件非常轻松的事情,让我们坚持一下把这个轻松的事情搞定。由于篇幅所限,笔者在这里的讲解将会省略掉非本章的内容。

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] colorParam:@"brown"];
    [self doWithCattleId:cattle[1] colorParam:@"red"];
    [self doWithCattleId:cattle[2] colorParam:@"black"];
    [self doWithCattleId:self colorParam:@"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 colorParam:(NSString*) color
{
    if(notFirstRun == NO)
    {
        NSString *myName = NSStringFromSelector(_cmd);
        NSLog(@"Running in the method of %@", myName);
        notFirstRun = YES;
    }
    
    NSString *cattleParamClassName = [aCattle className];
    if([cattleParamClassName isEqualToString:BULL_CLASS] || 
       [cattleParamClassName isEqualToString:CATTLE_CLASS])
    {
        [aCattle setLegsCount:4];
        if([aCattle respondsToSelector:skin])
        {
            [aCattle performSelector:skin withObject:color];
        }
        else
        {
            NSLog(@"Hi, I am a %@, have not setSkinColor!", cattleParamClassName);
        }
        [aCattle performSelector:say];
    }
    else
    {
        NSString *yourClassName = [aCattle className];
        NSLog(@"Hi, you are a %@, but I like cattle or bull!", yourClassName);
    }
}
@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的方式来进行这个牛股的设定,但是由于它的参数不是从NSObject继承下来的,所以我们无法使用。我们会有办法解决这个问题的,我们将在后面的章节里面介绍解决这个问题的方法。

第47行的代码,有一个非常重要NSObject的方法respondsToSelector ,通过向对象发送这个消息,加上一个SEL,我们可以知道这个对象是否可以相应这个SEL消息。由于我们的Cattle无法相应setSkinColor消息,所以如果对象是Cattle类生成的话,if语句就是NO所以花括号里面的内容不会得到执行。

第59行,我们通过类的名字发现了一个假冒的Cattle,我们把这个假冒的家伙给揪出来,然后实现了屏幕打印。

 

5.9,本章总结

本章给同学们介绍了几个新的数据类型,以及使用方法,这些数据类型分别是BOOL,SEL,Class,IMP。

 

本章的内容很重要,希望同学们花一点时间仔细的理解一下。应该说,本章的内容有些令人难以理解,或者说知道了SEL,Class,IMP之后也许不 知道如何使用,遇到什么情况的时候需要使用。不过在学习Objective-C的初级阶段,不知道这些也没有关系,但是SEL,Class,IMP的概念 需要掌握,否则当你遇到别人写的质量比较高的代码或者苹果官方的技术文档的时候,你会觉得理解起来比较吃力。

 

本章内容也是理解下一章内容的基础,下一章我们将要讲述NSObject的奥秘。

谢谢大家!

 

来源:http://www.cnblogs.com/yaski/archive/2009/04/05/1429735.html

 

 

ps:

 

SEL和IMP的用法

SEL是“selector”的一个类型,表示一个方法的名字。@selector是一个选择器,里面的参数是一个方法,返回的是SEL类型,也就是方法的名字。

IMP定义为 id (*IMP) (id, SEL, …)。这样说来, IMP是一个指向函数的指针,这个被指向的函数包括id(“self”指针),调用的SEL(方法名),再加上一些其他参数。说白了IMP就是实现方法。

下面是如何使用的示例:

 

- (void)methodWithInt:(int)firstInt andInt:(int)secondInt
{
NSLog(@"%d", firstInt + secondInt);
}
 
SEL theSelector = @selector(methodWithInt:andInt:);
IMP theImplementation = [self methodForSelector:theSelector];
 

其中,methodForSelector是NSObject中的方法,返回参数中指定的方法的入口地址,说白了,就是函数指针。

 

setSkinColor_Func=(void (*)(id, SEL, NSString*)) [cattle[1] methodForSelector:skin]; //强制转化为setSkinColor_Fun定义的void (*)(id, SEL, NSString*)类型指针 这们可以将这里的cattle[1]实例变量换成一个拥有-setSkinColor:(int) intVar签名的方法来试一下,这里并不强制转化为etSkinColor_Fun定义的函数类型指针
 IMP impVar =[someclass methodForSelector:skin];
然后我们求出comeclass下-setSkinColor:(int) intVar方法的地址,看看impVar的地址是否与someclass下的-setSkinColor:(int) intVar方法的地址是相同的

 

 

 

 

 

 

 

 

 

 


    
[3] Java版A星算法兑现步骤
    来源: 互联网  发布时间: 2014-02-18
Java版A星算法实现步骤

A星算法步骤: 1.起点先添加到开启列表中。 2.开启列表中有节点的话,取出第一个节点,即最小F值的节点, 判断此节点是否是目标点,是则找到了,跳出, 根据此节点取得八个方向的节点,求出G,H,F值, 判断每个节点在地图中是否能通过,不能通过则加入关闭列表中,跳出判断每个节点是否在关闭列表中,在则跳出, 判断每个节点是否在开启列表中,在则更新G值,F值,还更新其父节点;不在则将其添加到开启列表中,计算G值,H值,F值,添加其节点。 3.把此节点从开启列表中删除,再添加到关闭列表中。 4.把开启列表中按照F值最小的节点进行排序,最小的F值在第一个。 5.重复2,3,4步骤, 直到目标点在开启列表中,即找到了;目标点不在开启列表中,开启列表为空,即没找到

 

//A*算法public class AStar {  
    private int[][] map;//地图(1可通过 0不可通过)  
    private List<Node> openList;//开启列表  
    private List<Node> closeList;//关闭列表  
    private final int COST_STRAIGHT = 10;//垂直方向或水平方向移动的路径评分  
    private final int COST_DIAGONAL = 14;//斜方向移动的路径评分  
    private int row;//行  
    private int column;//列  
        public AStar(int[][] map,int row,int column){  
        this.map=map;  
        this.row=row;  
        this.column=column;  
        openList=new ArrayList<Node>();  
        closeList=new ArrayList<Node>();  
    }  
        //查找坐标(-1:错误,0:没找到,1:找到了)  
    public int search(int x1,int y1,int x2,int y2){  
        if(x1<0||x1>=row||x2<0||x2>=row||y1<0||y1>=column||y2<0||y2>=column){  
              
return -1;  
        }  
        if(map[x1][y1]==0||map[x2][y2]==0){            return -1;  
        }  
        Node sNode=new Node(x1,y1,null);  
        Node eNode=new Node(x2,y2,null);        openList.add(sNode);  
        List<Node> resultList=search(sNode, eNode);  
        if(resultList.size()==0){  
            return 0;  
        }  
        for(Node node:resultList){  
            map[node.getX()][node.getY()]=-1;  
        }  
        return 1;  
    }  
        //查找核心算法  
    private List<Node> search(Node sNode,Node eNode){  
        List<Node> resultList=new ArrayList<Node>();  
        boolean isFind=false;  
        Node node=null;  
        while(openList.size()>0){  
            //取出开启列表中最低F值,即第一个存储的值的F为最低的  
            node=openList.get(0);  
            //判断是否找到目标点  
            if(node.getX()==eNode.getX()&&node.getY()==eNode.getY()){  
                isFind=true;  
                break;  
            }  
            //上  
            if((node.getY()-1)>=0){                checkPath(node.getX(),node.getY()-1,node, eNode, COST_STRAIGHT);  
            }  
            //下  
            if((node.getY()+1)<column){  
                checkPath(node.getX(),node.getY()+1,node, eNode, COST_STRAIGHT);  
            }  
            //左  
            if((node.getX()-1)>=0){  
                checkPath(node.getX()-1,node.getY(),node, eNode, COST_STRAIGHT);  
            }  
            //右  
            if((node.getX()+1)<row){  
                checkPath(node.getX()+1,node.getY(),node, eNode, COST_STRAIGHT);  
            }  
            //左上  
            if((node.getX()-1)>=0&&(node.getY()-1)>=0){  
                checkPath(node.getX()-1,node.getY()-1,node, eNode, COST_DIAGONAL);  
            }  
            //左下  
            if((node.getX()-1)>=0&&(node.getY()+1)<column){  
                checkPath(node.getX()-1,node.getY()+1,node, eNode,COST_DIAGONAL);  
            }  
            //右上  
            if((node.getX()+1)<row&&(node.getY()-1)>=0){                checkPath(node.getX()+1,node.getY()-1,node, eNode, COST_DIAGONAL);  
            }  
            //右下  
            if((node.getX()+1)<row&&(node.getY()+1)<column){  
                checkPath(node.getX()+1,node.getY()+1,node, eNode, COST_DIAGONAL);  
            }  
            //从开启列表中删除  
            //添加到关闭列表中  
            closeList.add(openList.remove(0));  
            //开启列表中排序,把F值最低的放到最底端            Collections.sort(openList, new NodeFComparator());  
        }  
        if(isFind){  
            getPath(resultList, node);  
        }  
        return resultList;  
    }  
        //查询此路是否能走通  
    private boolean checkPath(int x,int y,Node parentNode,Node eNode,int cost){  
        Node node=new Node(x, y, parentNode);  
        //查找地图中是否能通过  
        if(map[x][y]==0){  
            closeList.add(node);  
            return false;  
        }  
        //查找关闭列表中是否存在  
        if(isListContains(closeList, x, y)!=-1){  
            return false;  
        }  
        //查找开启列表中是否存在  
        int index=-1;  
        if((index=isListContains(openList, x, y))!=-1){  
            //G值是否更小,即是否更新G,F值  
            if((parentNode.getG()+cost)<openList.get(index).getG()){  
                node.setParentNode(parentNode);  
                countG(node, eNode, cost);  
                countF(node);  
                openList.set(index, node);  
            }  
        }else{  
            //添加到开启列表中  
            node.setParentNode(parentNode);            count(node, eNode, cost);  
            openList.add(node);  
        }  
        return true;  
    }  
        //集合中是否包含某个元素(-1:没有找到,否则返回所在的索引)  
    private int isListContains(List<Node> list,int x,int y){  
        for(int i=0;i<list.size();i++){  
            Node node=list.get(i);  
            if(node.getX()==x&&node.getY()==y){  
                return i;  
            }  
        }  
        return -1;  
    }  
        //从终点往返回到起点  
    private void getPath(List<Node> resultList,Node node){  
        if(node.getParentNode()!=null){            getPath(resultList, node.getParentNode());  
        }  
        resultList.add(node);  
    }  
        //计算G,H,F值  
    private void count(Node node,Node eNode,int cost){  
        countG(node, eNode, cost);  
        countH(node, eNode);  
        countF(eNode);  
    }  
    //计算G值  
    private void countG(Node node,Node eNode,int cost){  
        if(node.getParentNode()==null){  
            node.setG(cost);  
        }else{  
            node.setG(node.getParentNode().getG()+cost);  
        }  
    }  
    //计算H值  
    private void countH(Node node,Node eNode){  
        node.setF(Math.abs(node.getX()-eNode.getX())+Math.abs(node.getY()-eNode.getY()));  
    }  
    //计算F值  
    private void countF(Node node){  
        node.setF(node.getG()+node.getF());  
    }  
    }//节点类class Node {  
    private int x;//X坐标  
    private int y;//Y坐标  
    private Node parentNode;//父类节点  
    private int g;//当前点到起点的移动耗费  
    private int h;//当前点到终点的移动耗费,即曼哈顿距离|x1-x2|+|y1-y2|(忽略障碍物)  
    private int f;//f=g+h  
        public Node(int x,int y,Node parentNode){        this.x=x;  
        this.y=y;  
        this.parentNode=parentNode;  
    }  
        public int getX() {  
        return x;  
    }  
    public void setX(int x) {  
        this.x = x;  
    }  
    public int getY() {  
        return y;  
    }  
    public void setY(int y) {  
        this.y = y;  
    }  
    public Node getParentNode() {  
        return parentNode;  
    }  
    public void setParentNode(Node parentNode) {  
        this.parentNode = parentNode;  
    }  
    public int getG() {  
        return g;  
    }  
    public void setG(int g) {  
        this.g = g;  
    }  
    public int getH() {  
        return h;  
    }  
    public void setH(int h) {  
        this.h = h;  
    }  
    public int getF() {  
        return f;  
    }  
    public void setF(int f) {  
        this.f = f;  
    }}//节点比较类class NodeFComparator implements Comparator<Node>{  
    @Override 
    public int compare(Node o1, Node o2) {  
        return o1.getF()-o2.getF();  
    }  
    }  


测试类:


public class Test {  
        public static void main(String[] args){
        int[][] map=new int[][]{
// 地图数组
                {1,1,1,1,1,1,1,1,1,1},
                {1,1,1,1,0,1,1,1,1,1},
                {1,1,1,1,0,1,1,1,1,1},
                {1,1,1,1,0,1,1,1,1,1},
                {1,1,1,1,0,1,1,1,1,1},
                {1,1,1,1,0,1,1,1,1,1}  
        };  
        AStar aStar=new AStar(map, 6, 10);  
        int flag=aStar.search(4, 0, 3, 8);  
        if(flag==-1){  
            System.out.println("传输数据有误!");  
        }else if(flag==0){  
            System.out.println("没找到!");  
        }else{  
            for(int x=0;x<6;x++){  
                for(int y=0;y<10;y++){
                    if(map[x][y]==1){
                        System.out.print(" ");
                    }else if(map[x][y]==0){
                        System.out.print("〓");
                    }else if(map[x][y]==-1){
                        System.out.print("※");  
                    }  
                }  
                System.out.println();  
            }  
        }  
    }} 
 
 

    
最新技术文章:
▪Android开发之登录验证实例教程
▪Android开发之注册登录方法示例
▪Android获取手机SIM卡运营商信息的方法
▪Android实现将已发送的短信写入短信数据库的...
▪Android发送短信功能代码
▪Android根据电话号码获得联系人头像实例代码
▪Android中GPS定位的用法实例
▪Android实现退出时关闭所有Activity的方法
▪Android实现文件的分割和组装
▪Android录音应用实例教程
▪Android双击返回键退出程序的实现方法
▪Android实现侦听电池状态显示、电量及充电动...
▪Android获取当前已连接的wifi信号强度的方法
▪Android实现动态显示或隐藏密码输入框的内容
▪根据USER-AGENT判断手机类型并跳转到相应的app...
▪Android Touch事件分发过程详解
技术文章 iis7站长之家
▪Android程序设计之AIDL实例详解
▪Android显式启动与隐式启动Activity的区别介绍
▪Android按钮单击事件的四种常用写法总结
▪Android消息处理机制Looper和Handler详解
▪Android实现Back功能代码片段总结
▪Android实用的代码片段 常用代码总结
▪Android实现弹出键盘的方法
▪Android中通过view方式获取当前Activity的屏幕截...
▪Android提高之自定义Menu(TabMenu)实现方法
▪Android提高之多方向抽屉实现方法
▪Android提高之MediaPlayer播放网络音频的实现方法...
▪Android提高之MediaPlayer播放网络视频的实现方法...
▪Android提高之手游转电视游戏的模拟操控
 


站内导航:


特别声明:169IT网站部分信息来自互联网,如果侵犯您的权利,请及时告知,本站将立即删除!

©2012-2021,,E-mail:www_#163.com(请将#改为@)

浙ICP备11055608号-3