网站业务功能设计,国外网站配色,入替皮杖 wordpress,wordpress微站前一篇关于NSProxy代理涉及到的关于消息转发#xff0c;把以前写的runtime文章从github上转移过来。一共三篇#xff0c;似乎自己也忘记了一些runtime的细节#xff0c;需要温故一下。 一、什么是Objc的Runtime#xff1f; Runtime是Objc语言的磐石#xff0c;Objc语言得以… 前一篇关于NSProxy代理涉及到的关于消息转发把以前写的runtime文章从github上转移过来。一共三篇似乎自己也忘记了一些runtime的细节需要温故一下。 一、什么是Objc的Runtime Runtime是Objc语言的磐石Objc语言得以运行也是依靠runtime库的支持。 Objc语言是一门动态语言它将很多静态语言在编译和链接时期做的事放到了运行时来处理。这种动态语言的优势在于我们写代码时能够更具灵活性如我们可以把消息转发给我们想要的对象或者随意交换一个方法的实现等。这种特性意味着Objc不仅需要一个编译器还需要一个运行时系统来执行编译的代码。对于Objc来说这个运行时系统就像一个操作系统一样它让所有的工作可以正常的运行这个运行时系统即Objc Runtime。 Runtime基本上是用C和汇编写的这个库使得C语言有了面向对象的能力。在这个库中对象可以用C语言中的结构体表示而方法可以用C函数来实现再加上了一些额外的特性。这些结构体和函数被runtime函数封装后我们就可以在程序运行时创建检查修改类、对象和它们的方法了。runtime可以有效的帮助我们为程序增加很多动态的行为。 二、Runtime中的重要概念 先看一句最常见的方法调用代码[receiver message]; 刚开始学习Objc时觉得上面的方法就是调用receiver对象的message方法。这样的理解也没错。更底层的原理应该是这样的 向receiver对象发送消息上面的代码会被编译器转成 objc_msgSend(receiver, selector) 如果含有多个参数则是 objc_msgSend(receiver, selector, arg1, arg2, ...) 如果消息的接收者receiver能够找到对应的selector那就执行接收者receiver的这个message方法。否则消息将被转发或临时动态的向接收者receiver添加这个selector的实现或者直接崩溃。 至此知道了编译阶段只是知道了向receiver对象发送了这样一个消息至于消息能否被响应需要等到运行时的具体情况决定。由此看出Objc的Runtime铸就了它动态语言的特性。 大部分情况下你就只管写你的Objc代码就行runtime系统自动在幕后辛勤劳作着。消息的执行会使用到一些编译器为实现动态语言特性而创建的数据结构和函数Objc中的类、方法和协议等在runtime中都由一些数据结构来定义。 三、梳理objc_msgSend 方法原型是id objc_msgSend(id self, SEL op, ...)。 id id是指向类实例的结构体指针。定义如下: typedef struct objc_object *id; id这个结构体的定义本身就带了一个*号, 所以我们在使用其他NSObject类型的实例时需要在前面加上* 而使用id时却不用。 接着我们发现一个objc_object的定义继续深入进去看下.. objc_object objc_object其实是一个结构体结构体里包含一个指向Class类的isa指针。根据isa指针就可以找到id实例所属的Class类了。 struct objc_object { Class isa; }; 发现一个Class的定义继续深入进去看下.. Class Class是一个指针类型指向了objc_class类型。 typedef struct objc_class *Class; 发现一个objc_class的定义继续深入进去看下.. objc_class 这个objc_class可是一个重要大明星。也是runtime的重点。到这里我们可以得出一个结论每个id对象都有一个指向所属类的指针isa。通过该指针对象可以找到它所属的类也可以找到了其全部父类(这一句需要在学完objc_class结构体后得到不过本篇不打算再说objc_class了因为篇幅有限重点需要另开一篇来学习总结)。 花开两朵各表一枝这条线先挖到objc_class这里。然后回头继续看objc_msgSend的第二个参数SEL类型的op。 SEL SEL其实是selector方法选择器的标示换言之SEL是用来标示selector的。 typedef struct objc_selector *SEL; Objc在编译时会依据每一个方法的名字、参数序列生成一个唯一的整型标识(Int类型的地址)这个标识就是SEL。如下代码所示 SEL sel selector(method);
NSLog(sel: %p, sel);//output:sel: 0x108dfbba3 两个类之间不管它们是父类与子类的关系还是之间没有这种关系只要方法名相同那么它的SEL就是一样的。每一个方法都对应着一个SEL。编译器会根据每个方法的方法名为那个方法生成唯一的SEL。这些SEL组成了一个Set集合当我们在这个集合中查找某个方法时只需要去找这个方法对应的SEL即可。而SEL本质是一个字符串所以直接比较它们的地址即可。 当然不同的类可以拥有相同的selector。不同类的实例对象执行相同的selector时会在各自的方法列表中去根据selector去寻找自己对应的IMP。 objc_msgSend的调用过程先理解其中的概念 当向一个对象发送消息时objc_msgSend方法根据对象的isa指针找到对象的类然后在类的调度表dispatch table中查找selector。如果无法找到selectorobjc_msgSend通过指向父类的指针找到父类并在父类的调度表dispatch table中查找selector以此类推直到NSObject类。一旦查找到selectorobjc_msgSend方法根据调度表的内存地址调用该实现。 通过这种方式message与方法的真正实现在执行阶段才绑定。 为了保证消息发送与执行的效率系统会将全部selector和使用过的方法的内存地址缓存起来。每个类都有一个独立的缓存缓存包含有当前类自己的 selector以及继承自父类的selector。查找调度表dispatch table前消息发送系统首先检查receiver对象的缓存。 上面的描述如下图所示 至此初步认识了runtime也初步了解了Objc的重要概念内容比如id、SEL等。当然更深的东西还要继续学习挖掘~~~ 转载于:https://www.cnblogs.com/vokie/p/9282801.html