`
miss大为
  • 浏览: 82505 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Runtime (一) 之 —— Class、Selector、IMP 等概念

阅读更多

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

 

我们在实际上的编程过程中,也许会遇到这样的场景,那就是我们在写程序的时候不能确切的知道我们需要使用什么类,使用这个类的什么方法。在这个时候,我们需要在我们的程序里面动态的根据用户的输入来创建我们在写程序不知道的类的对象,并且调用这个对象的实例方法。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”文件的下列代码:

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

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

 

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

 

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

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

1     SEL 变量名 = @selector(方法名字);
2     SEL 变量名 = NSSelectorFromString(方法名字的字符串);
3     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” 的下列代码片断:

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。在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类型,具体的使用格式如下:

 

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

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

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

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

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

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

 

 

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

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

1 - (void) doWithCattleId:(id) aCattle colorParam:(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] colorParam:@"brown"];
20     [self doWithCattleId:cattle[1] colorParam:@"red"];
21     [self doWithCattleId:cattle[2] colorParam:@"black"];
22     [self doWithCattleId:self colorParam:@"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 colorParam:(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 *cattleParamClassName = [aCattle className];
43     if([cattleParamClassName isEqualToString:BULL_CLASS] || 
44        [cattleParamClassName isEqualToString:CATTLE_CLASS])
45     {
46         [aCattle setLegsCount:4];
47         if([aCattle respondsToSelector:skin])
48         {
49             [aCattle performSelector:skin withObject:color];
50         }
51         else
52         {
53             NSLog(@"Hi, I am a %@, have not setSkinColor!", cattleParamClassName);
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的方式来进行这个牛股的设定,但是由于它的参数不是从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的奥秘。

谢谢大家! 

分享到:
评论

相关推荐

    objc runtime 源码 208

    在runtime中,每个方法都是一个结构体,包含了SEL(selector)、IMP(实现指针)和其他信息。`method_copyArgumentType`和`method_copyReturnType`可以获取方法的参数类型和返回类型,`method_...

    runtime介绍

    runtime在Objective-C中扮演着极其重要的角色,它允许程序在运行时动态地处理类、对象和方法等概念。 ### runtime简介 runtime是Objective-C的核心组件之一,它为Objective-C提供了强大的动态性。runtime系统提供...

    ios runtime动态添加方法demo

    本文将深入探讨“ios runtime动态添加方法demo”这一主题,通过一个简单的示例项目RuntimeDemo来阐述Runtime的核心概念和应用。 Runtime,全称为Objective-C Runtime,是Apple的Objective-C编程语言的一个关键组成...

    runtime10种用法

    Method method = class_addMethod([self class], selector, imp, types.UTF8String); if (!method) { // 方法已经存在 } } ``` #### 八、对象类型查询 在运行时获取对象的类型信息,这对于调试和分析非常有用。...

    iOS Runtime知识

    class_addMethod(newClass, @selector(myCustomMethod), (IMP)myCustomMethodImplementation, "v@:"); class_addProperty(newClass, "customProperty", &properties, sizeof(properties)); objc_...

    Runtime面试题.pdf

    iOS runtime是Objective-C语言的核心部分,负责对象、类、方法等的动态行为。Runtime面试题通常用于考察面试者对iOS底层机制的理解深度。下面将对给出的内容逐条进行知识点梳理。 1. isa指针 isa指针是Objective-C...

    Runtime 方法的替换(不同类).zip

    在编程领域,特别是Objective-C(OC)中,"Runtime 方法的替换(不同类)"是一个重要的概念,涉及到程序运行时的动态特性。Objective-C的运行时系统(Runtime System)赋予了开发者在程序运行过程中修改和替换类的方法...

    ios-Runtime.zip

    iOS中的Runtime系统是苹果开发的一种运行时环境,它在Objective-C和Swift等编程语言中扮演着核心角色。Runtime系统提供了一种方式,让程序在运行过程中可以动态地获取类、对象、方法等信息,甚至可以改变它们的行为...

    (OC)RunTime获取类的对象方法

    Runtime系统是OC能够实现消息传递、动态类型、多态等特性的重要基础。本文将详细探讨如何利用Runtime获取类的对象方法。 1. **Runtime概述** - Runtime是一套C语言的库,为OC提供了一种在程序运行过程中操作和改变...

    Runtime面试.pdf

    方法缓存用于快速访问经常调用的方法,它是一个增量扩展的哈希表,存储了SEL(selector)到IMP(方法实现)的映射。在objc-cache.mm文件中有详细的方法缓存结构和查找过程的实现。 7. 关联对象(Associated Object...

    runtime原理与使用

    Method newMethod = class_addMethod(myClass, @selector(newMethod), (IMP)newMethodImplementation, "v@:"); } @end void newMethodImplementation(id self, SEL _cmd) { NSLog(@"New method added dynamically...

    iOS开发中runtime常用的几种方法示例总结

    class_addMethod([person class], @selector(coding), (IMP) coding, "v@:"); 五、总结 在iOS开发中,runtime是一个非常强大的机制,可以帮助我们实现许多复杂的功能。通过本文,我们可以了解到runtime的基本概念...

    方法的交换(同类).zip

    `Method`是一个结构体,包含了方法的selector、IMP和类型编码等信息。交换方法的步骤通常是这样的: 1. 获取目标类的两个方法,可以使用`class_getInstanceMethod`或`class_getClassMethod`函数,根据需要交换实例...

    iOS Runntime 动态添加类方法并调用-class_addMethod

    class_addMethod(class, selector, (IMP)sayHello, types)) { NSLog(@"Method already exists."); } NSObject *object = [[NSObject alloc] init]; [object performSelector:selector]; // 调用新添加的方法 ...

    iOS runtime动态添加方法示例详解

    ### 一、动态添加方法的概念 动态添加方法的主要用途是在程序运行时增加类的功能,而无需重新编译或修改原有代码。这种技术特别适用于需要在运行时根据特定条件调整行为的情况。例如,如果你有一个类有很多方法,...

    iOS利用运行时处理闪退

    数组操作不当是iOS应用闪退的常见原因之一。当我们尝试访问超出数组范围的索引时,程序会崩溃。为防止这类问题,应该始终在访问数组元素前进行边界检查。如上所述,我们可以添加新的方法来确保安全的数组访问: ```...

    iOS Runtime详解(新手也看得懂)

    iOS Runtime 是Objective-C编程语言的核心组成部分,它是一个运行时库,负责实现Objective-C的动态特性,如消息传递、动态类型和方法解析等。本文将深入探讨Runtime的基本概念、消息传递机制以及消息转发的过程。 ...

    Mehod Swizzling实现页面统计功能

    2. `class_replaceMethod(Class aClass, SEL aSelector, IMP aImplementation, const char *types)`: 添加一个新的方法实现,如果已有同名方法则会替换原有实现。 3. `method_exchangeImplementations(Method m1, ...

    ExchangeTestApp.zip

    在Objective-C中,我们可以使用`objc/runtime.h`库提供的API来实现方法交换,主要包括`method_exchangeImplementations()`和`class_replaceMethod()`两个函数。 1. `method_exchangeImplementations()`:这个函数...

Global site tag (gtag.js) - Google Analytics