SEL selector (二)

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,”[email protected]: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,”[email protected]: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的支持。

时间: 2024-08-05 10:47:51

SEL selector (二)的相关文章

Objective-C - SEL (@selector) 原理及本质

SEL (@selector) 原理及本质 /* 1.SEL其实是对方法的一种包装,将方法包装成一个SEL类型的数据,去找对应的方法地址.找到方法地址就可以调用方法 [email protected]()就是取类方法的编号,等同C语言的中函数指针,只不过C语言中,可以把函数名直接赋给一个函数指针,而Object-C的类不能直接应用函数指针,这样只能做一个@selector语法来取. 它的结果是一个SEL类型.这个类型本质是类方法的编号(函数地址) 3.其实消息就是SEL */ #import <

IOS SEL (@selector) 原理及使用总结(二)

SEL消息机制工作原理是什么 引用下面文章: 我们在之前有提到,一个类就像一个 C 结构.NSObject 声明了一个成员变量: isa. 由于 NSObject 是所有类的根类,所以所有的对象都会有一个 isa 的成员变量[公共继承].而该 isa 变量指向该对象的类(图3.15)[类在Objective-C中也是一个实体, 由于存在Objective-C 运行环境所有的类将有自己的存储空间.Objective-C 运行环境将为每个类分配空间. 这里 所说的 isa,正是指向这样一个类的空间.

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

SEL 类成员方法的指针 可以理解 @selector()就是取类方法的编号,他的行为基本可以等同C语言的中函数指针,只不过C语言中,可以把函数名直接赋给一个函数指针,而Object-C的类不能直接应用函数指针,这样只能做一个@selector语法来取. 它的结果是一个SEL类型.这个类型本质是类方法的编号(函数地址) C/C++函数指针 int test(int val) {return val+1; } int (* c_func)(int val); //定义一个函数指针变量c_func

SEL selector

SEL 类成员方法的指针 可以理解 @selector()就是取类方法的编号,他的行为基本可以等同C语言的中函数指针,只不过C语言中,可以把函数名直接赋给一个函数指针,而Object-C的类不能直接应用函数指针,这样只能做一个@selector语法来取. 它的结果是一个SEL类型.这个类型本质是类方法的编号(函数地址) C/C++函数指针 int test(int val) {return val+1; } int (* c_func)(int val); //定义一个函数指针变量c_func

OC SEL (@selector) 原理及使用总结

@import url(http://i.cnblogs.com/Load.ashx?type=style&file=SyntaxHighlighter.css);@import url(/css/cuteeditor.css); SEL 类成员方法的指针 可以理解 @selector()就是取类方法的编号,他的行为基本可以等同C语言的中函数指针,只不过C语言中,可以把函数名直接赋给一个函数指针,而Object-C的类不能直接应用函数指针,这样只能做一个@selector语法来取. 它的结果是一

iOS开发——语法篇OC篇&amp;高级语法精讲二

Objective高级语法精讲二 Objective-C是基于C语言加入了面向对象特性和消息转发机制的动态语言,这意味着它不仅需要一个编译器,还需要Runtime系统来动态创建类和对象,进行消息发送和转发.下面通过分析Apple开源的Runtime代码(我使用的版本是objc4-646.tar)来深入理解Objective-C的Runtime机制. Runtime数据结构 在Objective-C中,使用[receiver message]语法并不会马上执行receiver对象的message方

python学习(二)百度爬虫0.1

参照着网上的爬虫案例(点我),先做了一个demo,基本的爬虫项目创建,以及数据抽取,数据分析,数据保存等等过程基本上有所掌握. 我的需求是需要检索指定的百度贴吧,根据指定的关键字库,搜索出含有关键字的链接,并抽取出来,用于后续告警. 因此,基于需求,分如下步骤: 第一:基于Scrapy创建爬虫项目: 第二:新建TieBaSpider爬虫: 第三:新建外部关键字库dictionary.txt文件,贴吧地址配置url.txt文件: 第一步参考晚上案例. 从第二步开始,编写爬虫,同时创建实例对象以及创

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

一:description方法 description有对象方法和类方法两种,(是NSObject类的方法) 1,-description(对象方法) 使用NSLog和@%输出某个对象时,会调用对象的description方法,并拿到返回值进行输出.(系统会自动调用打印对象的description方法) 而如果打印NSString对象的话,默认返回的就是NSString字符串 2,+description (类方法) 使用NSLog和@%输出某个类时,会调用类的description类方法,并拿

runtime总结二之消息机制(包括消息转发,消息交换的黑魔法)

runtime的消息机制 前面提到过编译器最终会把我们的消息发送转化为函数调用 消息发送 [object sendMassage] 首先编译器会在运行时将上面的例子转化为objc_msgSend(obj,@selector(sendMassage))这个函数,转换的时候除了方法本身的参数之外,还有两个隐藏的参数一个是id类型的,代表对象的类型,还是一个是SEL类型的,是函数对应的方法的编号,接下来就会按照下面的流程来调用这个方法 通过obj的isa指针找到其所对应的类. 通过SEL先去类的cac