博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Objective-C中的isa、class、SEL、IMP
阅读量:6494 次
发布时间:2019-06-24

本文共 4520 字,大约阅读时间需要 15 分钟。

  hot3.png

在objective c中,如果细心的话会发现,每个类中都会自动生成一个class 类型的isa,

@interface NSObject 
 {       Class    isa;   }

isa是什么,class又是什么呢,找到Class的定义我们会发现如下:

typedefstruct objc_class *Class;

objc_class以前的定义又如下,现在据说被封闭了,不知道有没有再作修改,总之方便我们理解就好:

struct objc_class {       Class isa;              Class super_class;          const char *name;       long version;       long info;         long instance_size;       struct objc_ivar_list *ivars;       struct objc_method_list **methodLists;        struct objc_cache *cache;       struct objc_protocol_list *protocols;      }

    于是我们就有了点头绪了,isa,is a pointer,是个指针,每个类都有一个class类型的指针isa,继承自NSObject中,继承关系,方法变量等信息都存放在isa中,isa作为一个隐藏的属性,会自动生成于每个类之中。有了这个前提,也就可以解释为什么我们可以根据@class来代替任意一个类了,看代码:

Human.h

#import 
    @interface Human : NSObject  -(void)say;  @end

Human.m

#import "Human.h"    @implementation Human  -(void)say  {      NSLog(@"Human中的say方法");  }  @end

main.h

#import 
  #import "Human.h"  int main(int argc, const char * argv[])  {        @autoreleasepool {                    Class c =NSClassFromString(@"Human");          [[c new] say];          //以上CLASS类型的c,就相当于Human类。      }      return 0;  }

    class可以灵活的代替别的类,SEL与其类似,不同的是SEL代替的是方法,可以方便的代替其他方法,class中是因为有isa属性保存有类的信息,而SEL是因为即使是在不同的类中,方法名只要相同,这两个方法的ID就相同,SEL就是根据这个ID来找到该方法,再根据调用该方法的类的不同来找到唯一的地址。

typedef struct objc_object *id;typedef struct objc_selector *SEL;#if !OBJC_OLD_DISPATCH_PROTOTYPEStypedef void (*IMP)(void /* id, SEL, ... */ ); #elsetypedef id (*IMP)(id, SEL, ...); #endif

    从上面的头文件中我们可以看到,IMP定义为 id (*IMP) (id, SEL, …)。这样说来, IMP是一个指向函数的指针,这个被指向的函数包括id(“self”指针),调用的SEL(方法名),再加上一些其他参数。 IMP 就是一个函数指针,这个被指向的函数包含一个接收消息的对象id(self  指针), 调用方法的选标 SEL (方法名),以及不定个数的方法参数,并返回一个id。也就是说 IMP 是消息最终调用的执行代码,是方法真正的实现代码 。

 #if !OBJC_OLD_DISPATCH_PROTOTYPESOBJC_EXPORT void objc_msgSend(void /* id self, SEL op, ... */ )    __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);OBJC_EXPORT void objc_msgSendSuper(void /* struct objc_super *super, SEL op, ... */ )    __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);#else

Method

typedef struct objc_method *Method;
typedef struct objc_method *Method;struct objc_method {  SEL method_name;  char *method_types;  IMP method_imp;};

    这个定义看上去包括了我们上面说过的其他类型。也就是说,Method(我们常说的方法)表示一种类型,这种类型与selector和实现(implementation)相关。

看代码再作解释:

#import 
    @interface Human : NSObject  -(void)say;  @end  @implementation Human  -(void)say  {      NSLog(@"Human中的say方法");  }  @end

//上面定义了一个human类,里面有一个say方法  

#import 
  @interface man:NSObject  {}  -(void)say; @end    @implementation man  -(void)say  {      NSLog(@"man中的say方法");  }  @end

//在上面定义了一个man类,同样有一个say方法  

int main(int argc, const char * argv[])  {        @autoreleasepool {                    Class a =NSClassFromString(@"Human");          Class b =NSClassFromString(@"man");          //根据方法名say找到该方法的id,将sel与其绑定;          SEL sel = NSSelectorFromString(@"say");          [[a new] performSelector:sel];          [[b new] performSelector:sel];         }      return 0;  }

结果如下:

2012-03-13 10:13:24.900 String[2725:403] Human中的say方法  2012-03-13 10:13:24.901 String[2725:403] man中的say方法

    通过以上代码我们会发现,SEL通过方法名绑定后,可以被多个类实例调用,找了些网上的资料,解释都是说方法名一样的话,ID会一样,地址仍不同,才会实现这样的效果,我们不谈论是否准确,但我个人认为这是目前最合理的解释。这种用法的优势一方面是灵活性更高,类似于多态,另一方面是,这种用法sel找方法时匹配的是ID而不是字符串方法名,所以在效率上会高一些。还有一种更终极的方法,直接对应方法的地址,这种方法效率最高,请看代码:

#import 
    @interface Human : NSObject  -(void)say;  @end  @implementation Human  -(void)say  {      NSLog(@"Human中的say方法");  }  @end

//上面定义了一个human类,里面有一个say方法  

#import 
  @interface man:NSObject  {}  -(void)say; @end    @implementation man  -(void)say  {      NSLog(@"man中的say方法");  }  @end

//在上面定义了一个man类,同样有一个say方法  

int main(int argc, const char * argv[])  {        @autoreleasepool {                    Human *human =[Human new];          man *ma=[man new];          //根据方法名say找到该方法的id,将sel与其绑定;          SEL sel =@selector(say);//也可以这样写:SEL sel=NSSelectorFromString(@"say");          IMP imp1 = [human methodForSelector:sel];               IMP imp2 = [ma methodForSelector:sel];                 imp1(human,sel);          imp2(ma,sel);          //因为每个方法都有自己的地址,这种方式直接找到地址区分相同ID的方法,效率最高,但灵活性不如SEL方式。                }      return 0;  }

输出语句:

2012-03-13 10:35:21.446 String[3763:403] Human中的say方法  2012-03-13 10:35:21.450 String[3763:403] man中的say方法

    今天这些内容不太好理解,我用自己理解的方式给大家再解释一遍,class用于代替类,增加灵活性,因为我们不知道什么时候会用到什么类,方法也是如此,所以SEL可以代替方法,每个方法有方法名,ID,地址,相同的方法名,ID也一样,正常情况下我们根据方法名找到方法,用SEL方法可以根据ID找到方法,而用IMP方式可以直接找到地址,但是灵活性不如SEL方法,虽然效率最高。

转载于:https://my.oschina.net/Jacedy/blog/625219

你可能感兴趣的文章
Java设置以及获取JavaBean私有属性进阶
查看>>
db2表结构导出导入,数据库备份
查看>>
策略模式
查看>>
第二 周作业总结
查看>>
OrderOnline——项目概述
查看>>
POJ-2739(Water)
查看>>
【转】第三节 UNIX文件系统结构
查看>>
为什么sql里面not in后面的子查询如果有记录为NULL的,主查询就查不到记录
查看>>
Angular7里面实现 debounce search
查看>>
Linux 内核链表
查看>>
git学习------>Git 分支管理最佳实践
查看>>
括号和出栈所有序列问题
查看>>
第一次操刀数据库分表的教训与经验
查看>>
录音声音小
查看>>
Ubuntu 12.04 安装 Chrome浏览器
查看>>
java 反射
查看>>
ORACLE物化视图(物理视图)
查看>>
android 读取json数据(遍历JSONObject和JSONArray)(转)
查看>>
UIScrollView中的手势
查看>>
递归和迭代的差别
查看>>