SEL消息机制工作原理是什么

引用下面文章:

我们在之前有提到,一个类就像一个 C 结构.NSObject 声明了一个成员变量: isa. 由于 NSObject 是所有类的根类,所以所有的对象都会有一个 isa 的成员变量[公共继承].而该 isa 变量指向该对象的类(图3.15)[类在Objective-C中也是一个实体, 由于存在Objective-C 运行环境所有的类将有自己的存储空间.Objective-C 运行环境将为每个类分配空间. 这里 所说的 isa,正是指向这样一个类的空间. 从而建立类和对象之间的对应关系.] 类空间 包含了该类定义的成员变量,以及方法实现, 还包含了指向自己父类空间的指针.

方法以 selector 作为索引. selector 的数据类型是 SEL. 虽然 SEL 定义成 char*, 我们可 以把它想象成 int. 每个方法的名字对应一个唯一的 int 值.比如, 方法 addObject: 可能 对应的是 12. 当寻找该方法是, 使用的是 selector,而不是名字 @"addObject:"

Objective-C 数据结构中,存在一个 name - selector 的映射表如图 3.16

在编译的时候, 只要有方法的调用, 编译器都会通过 selector 来查找,所以 (假设 addObject 的 selector 为 12)

 [myObject addObject:yourObject]; 

将会编译变成

objc_msgSend(myObject, 12, yourObject);

这里,objec_msgSend()函数将会使用 myObjec 的 isa 指针来找到 myObject 的类空间结构并 在类空间结构中查找 selector 12 所对应的方法.如果没有找到,那么将使用指向父类的指 针找到父类空间结构进行 selector 12 的查找. 如果仍然没有找到,就继续往父类的父类一 直找,直到找到为止, 如果到了根类 NSObject 中仍然找不到,将会抛出异常.

我们可以看到, 这是一个很动态的查找过程.类的结构可以在运行的时候改变,这样可以很 容易来进行功能扩展[Objective-C 语言是动态语言, 支持动态绑定.

再来看

Objective-C如何获取消息的原理详解文章

Objective-C获取消息工作原理是本文要介绍的内容,看name mangling的时候,也讲到了Objective-C的name mangling,于是又重新读了一下Objective-C 2.0 programming Language以及Objective-C 2.0 Runtime Reference里的相关内容,自己归纳一下。

先贴一段代码:

[plain] view plaincopy
  1. MyClass.h
  2. @interface MyClass : NSObject
  3. {
  4. }
  5. @end
  6. MyClass.m
  7. #import
  8. #import “MyClass.h”
  9. void myClassIMP(id _rec, SEL _cmd, int theInt)
  10. {
  11. NSLog(@”dynamic added method:%d”,theInt);
  12. }
  13. - (id)init
  14. {
  15. if( ( self = [super init]) != nil )
  16. {
  17. class_addMethod([MyClass class], @selector(dynGeneratedMethod:),(IMP)myClassIMP,”v@:i”);
  18. }
  19. return self;
  20. }
  21. Main.c
  22. #import “MyClass.h”
  23. int main(int argc, char *argv[])
  24. {
  25. MyClass theInstance = [[MyClass alloc] init];
  26. [theInstance dynGeneratedMethod:10];
  27. return 0;
  28. }

这段代码执行的结果是在控制台上输出:

dynamic added method:10

接着来详细分析一下上面的代码:

在ObjC的类中这样的一个声明 – (void)foo:(int)a;被称作方法(method),而在调用的地方: [theClass foo:10];则被称之为发送消息(send message),具体来说是给对象theClass 发送foo:消息,注意这里foo后面的”:”,它也是消息名称的一部分,最前面的'-'代表实例方法,'+'代表类方法。而类似的语句,在C或C++ 中,通常被称为呼叫函数(call function),在ObjC中,函数(function)一词很少用到,不是它不存在,而是它被ObjC runtime给隐藏了起来。

如前所述,ObjC是以消息机制来工作的,但其实诸如-(void)foo:(int)a的语句在编译时被 objc_msgSend(receiver,selector,arg1,arg2,….)替换了,所以其实每一条发送消息的代码本质上还是调用函数 (call function),不过他们调用的都是同一个函数objc_msgSend(也可能是objc_msgSend_stret(返回值是结构 体),objc_msgSend_fpret(返回值是浮点型)等)

分析objc_msgSend的参数,第一个receiver的类型是id,代表接受消息的对象,第二个是selector代表接收对象的方法,后面的是该方法的参数,之前那条语句的被编译器替换后就是:

[theClass foo:10]  -> objc_msg(theClass,@selector(foo:),10);

因为消息的接受对象和接受对象的方法都参数化,所以在运行时刻,接受对象和接受对象的方法都可以是动态的!

比如说程序里面可以这样写:

id helper = getTheReceiver(); 
SEL request = getTheSelector(); 
[helper performSelector:request];

它的实现是基于ObjC runtime. NSObject类实现了这套机制,所以每一个继承于NSObject的类都能自动获得runtime的支持。在这样的一个类中,有一个isa指针,指向 该类定义的数据结构体,这个结构体是由编译器编译时为类(须继承于NSObject)创建的.在这个结构体中有包括了指向其父类类定义的指针以及 Dispatch table. Dispatch table是一张SEL和IMP的对应表。

对于名称相同的方法,他们都有相同的SEL,方法的名称不包括类名称,所以子类和父类中的同名方法拥有相同的SEL,但是他们的实现可以各不相同, 因而在他们各自的Dispatch表中SEL所对应的IMP是不同的,IMP是一个函数指针,而虽然每一个SEL对应的是一个方法的名称,但考虑到效 率,SEL本身是一个整型,编译器会另外生成一张SEL和方法名称对应的表。有了这样的结构,objc就可以实现多态了。还是这行代码:

[theClass foo:10];

是向theClass发送了foo:消息,那么首先在theClass的类结构的Dispatch table里找有没有对应的SEL,如果有的话,就表示theClass有响应该消息的方法,程序就跳到该方法的代码地址头(由IMP指定),开始执行。 如果在theClass的Dispatch table找不到对应的SEL,那么就会通过isa所指的结构体中包含的父类指针,到父类里面去寻找,如果到最后还是没有找到,就会出现runtime error.所以说,即使theClass以及它的父类都没有定义-(void) foo:(int)a方法,程序还是可以通过编译,但如果是用xcode的话,编译器会有警告,告知theClass可能无法响应该消息。不会报错的原因 是类的方法也可以在执行时刻创建!上面的代码:

class_addMethod([MyClass class], @selector(dynGeneratedMethod:),(IMP)myClassIMP,”v@:i”);

就是给MyClass类在执行时刻增加了一个响应dynGeneratedMethod:消息的方法,这样之后对任何MyClass的instance类 发送dynGeneratedMethod:消息,就会得到响应了.myClassIMP是类收到该消息时要调用的方法,其声明如下:

void myClassIMP(id _rec, SEL _cmd, int theInt)

这个方法的前面两个参数是必须的,之后的参数才是我们实际用到的参数,数目和@selector()中的冒号数一样,冒号数代表的就是参数个数。第一个参数是消息的接受对象,是MyClass的实例,第二个参数是由SEL代表的具体消息。

Class_addMethod的最后一个参数是表示dynGeneratedMethod:的返回值和参数信息,不过我自己试了一下,这个参数不起作用。

几个要点:

1、对于C中被称为函数(function)和函数调用(function call)的地方,在ObjC中被叫做方法(method)和发送消息(send message).试图调用未定义的方法会导致编译错误,而发送一条消息,即使没有任何类定义了响应该消息的方法,编译时也不会报错,从语义上讲这也是对 的,发一条消息本来就不要求一定有人会响应,不过如果执行到发送消息的代码时真的没有类可以响应的话,是会发生runtime error,为了避免这种事情发生,可以先进行检测,这样写

if( [myClass respondsToSelector:@selector(foo:)]) 
   [myClass foo:10]; 
}

我感觉ObjC这样的一套sender receiver的定义更注重面向对象的概念。类是一个接收者(receiver),如果定义了某个方法,就可以接收和这个方法名称相同的消息。而使用该 类的client(sender),则尝试向该类发送消息.如果匹配了,就跳到类的方法里执行。

2、方法名称是诸如foo:,不包括返回类型,参数类型,而又因为一个foo:对应于一个SEL,所以说ObjC不支持相同的foo:有不同的返回 类型,也不支持重载。不过类方法和实例方法可以有相同的名字,而又有不同类型的参数和返回类型,因为它们不是处在同一张dispatch table中。

3、不仅类的方法可以运行时刻创建,类本身也可以在运行时刻创建,前面提到继承于NSObject的类,编译器会帮忙生成ObjC runtime所需要的类结构定义,只要我们在代码里也按照那个结构创建了自己的类,那一样可以获得ObjC runtime的支持。

转载于:https://www.cnblogs.com/idLuffy/p/4036514.html

SEL selector (二)相关推荐

  1. IOS SEL (@selector) 原理及使用总结(一)

    SEL 类成员方法的指针 可以理解 @selector()就是取类方法的编号,他的行为基本可以等同C语言的中函数指针,只不过C语言中,可以把函数名直接赋给一个函数指针,而Object-C的类不能直接应 ...

  2. MOQL—筛选器(Selector)(二)

    FROM子句 描述了查询从哪些数据表中获取数据.语法如下: from TABLE [[as] ALIAS] [,...] [[inner|left|right|full] join] TABLE [[ ...

  3. iOS学习之Runtime(二)

    前面已经介绍了Runtime系统的概念.作用.部分技术点和应用场景,这篇将会继续学习Runtime的其他知识. 一.Runtime技术点之类/对象的关联对象 关联对象不是为类/对象添加属性或者成员变量 ...

  4. Object-C 中的Selector 概念

    selector可以叫做选择器,其实指的就是对象的方法,也可以理解为C语言里面的函数指针,在面向对象里面的对应概念. @selector(xxxx)的作用是找到名字为xxxx的方法.一般用于[a pe ...

  5. Selector 概念

       selector可以叫做选择器,其实指的就是对象的方法,也可以理解为C语言里面的函数指针,在面向对象里面的对应概念.    @selector(xxxx)的作用是找到名字为xxxx的方法.一般用 ...

  6. 【转】selector函数指针回调机制

    selector可以叫做选择器,其实指的就是对象的方法,也可以理解为C语言里面的函数指针,在面向对象里面的对应概念. [self performSelector:@selector(Hidden) w ...

  7. OC中description、 SEL、类本质、self和super用法

    一:description方法 description有对象方法和类方法两种,(是NSObject类的方法) 1,-description(对象方法) 使用NSLog和@%输出某个对象时,会调用对象的 ...

  8. iOS 开发学习之 User Interface(4)UIView 与 UIViewController【二】

    UIViewController 生命周期 从视图控制器创建,在屏幕上显示视图,再到被销毁的过程.一个生命周期内viewDidLoad只被调用一次.见图一 // 视图控制器在经过alloc 之后,开始 ...

  9. iOS开发中屏幕旋转(二)

    Morris_ 2019.04.11 前面有总结过一些在开发中遇到的屏幕旋转的基础知识. 一.设置应用支持的转屏方向 设置方式 00x1 在TARGET->General->Deploym ...

最新文章

  1. 新手必看:Python 3.8六大新功能
  2. 联想平板电脑win10如何取消触摸键盘按键声音
  3. spring security oauth rce (cve-2016-4977) 漏洞分析
  4. 添加MSDN搜索的正确方法
  5. QT绘制水平百分比条形图。
  6. CSS清除默认样式,技术详细介绍
  7. 李迟2021年9月知识总结
  8. Spring Boot 学习之配置文件 application.yml
  9. Spring事务管理,使用@Transactional注解
  10. 电力-101规约说明书2
  11. 程序员面试:未来五年的规划是怎样的?
  12. Node.JS全开源B2C商城
  13. 深度解读物联网区块链“IOTA”:不仅解决IoT痛点,还解决区块链痛点
  14. 国家标准计算机软件测试文档范文,软件测试相关国家标准.doc
  15. ANDROID物联网开发从入门到实战附源码
  16. 初中七年级计算机课教学总结怎么写,初中计算机教学工作总结范文
  17. 小猫爬山 (深搜+搜索顺序优化)
  18. HighTec编译器获取不到license常见原因
  19. opc服务器上层传输协议,OPC服务器 (OPC Server) 之间数据传递的桥梁 — OPC Data Manager (ODM)...
  20. stm32f4 adc 使用

热门文章

  1. java 工程新建ivy文件_Hadoop学习之路(八)在eclispe上搭建Hadoop开发环境
  2. 用python随机生成5000个网址_使用Python脚本生成随机IP的简单方法
  3. 牛客网数据开发题库_数据库刷题—牛客网(21-30)
  4. PCM复用设备和PDH设备的区别介绍
  5. 为什么要使用工业以太网交换机?
  6. 【渝粤教育】 国家开放大学2020年春季 1444药理学(本) 参考试题
  7. 【渝粤题库】陕西师范大学200131中国古代文论 作业(专升本)
  8. mysql 数据路由_node-路由操作mysql数据库
  9. 红米ac2100breed看模块_骁龙865加持,红米K30S至尊纪念版11日再开售
  10. weblogic创建域后启动不了_摩托车淋雨后启动不了什么原因?如何解决?