oc - runtime运行机制
Objective-C语言是一门动态语言,它将很多静态语言在编译和链接时做的事放到了运行时来处理。同时OC也是一门简单的语言,很大一部分是C的内容,只是在语言层面上加了关键字和语法,真正让OC强大的是它的运行时,它很小却很强大,其中核心是消息分发。这种动态语言的优势在于:我们写代码时更加灵活,如我们可以把消息转发给我们想要的对象,或者随意交换一个方法的实现。
这种特性意味着OC不仅需要一个编译器,还需要一个运行时系统来执行编译的代码。对于OC来说,这个运行时系统就像一个操作系统一样。这个运行时系统即Runtime,是用C和汇编写的,这个库使得C语言有了面向对象的能力。其中最主要的是消息机制。对于C语言,函数的调用在编译的会决定调用哪个函数,编译完成之后顺序执行,无任何二义性。OC的函数调用称为消息发送,属于动态调用过程。在编译的时候并不能决定真正的调用哪个函数(事实证明,在编译阶段,OC可以调用任何函数,即使这个函数并未实现,只要声明过就不会报错。而C语言在编译阶段就会报错。)只有在真正运行的时候才会根据函数的名称找到对应的函数来调用。
用一句话说:我们编写的OC代码,程序运行过程中,其实都是转化为runtime的C语言代码,runtime算是OC幕后工作者。runtime属于OC的底层,可以进行非常底层的操作(用OC是无法实现的。)
Runtime库主要做下面几件事:
1.封装:在这个库中,对象可以用C语言中的结构体表示,而方法可以用C函数来实现,另外再加上一些额外的特性。这些结构体和函数被Runtime函数封装后,我们就可以在程序运行时创建、检查、修改类、对象和他们的方法了。
2.找出方法的最终执行代码:当程序执行[object doSomething]时,会向消息接收者(object)发送一条消息(doSomething),Runtime会根据消息接收者是否能响应该消息而做出不同的反应。
OC的Runtime目前有两个版本:Modern Runtime和Legacy runtime. Modern Runtime主要用于64位应用。Legacy runtime主要用于32位应用。目前一般都是64位了。
关于执行效率问题,“静态语言执行效率要比动态语言高”应该是没问题的。因为一部分的CPU计算损耗在Runtime中。
那OC是怎么实现动态调用的呢?假如在OC中写了如下代码:
- [obj makeText];
其中obj是一个对象,makeText是一个函数名称。执行一个方法,有些语言,编译器会执行一些额外的优化和错误检查,因为调用关系很直接也很明显。但是对于消息分发来说,就不那么明显了,在发消息之前不必知道某个对象是否能够处理消息。你把消息发给他,他可能会处理,也可能转给其他的Object来处理。一个消息不必对应一个方法,一个对象可能实现一个方法来处理多条消息。
在编译时RunTime会将上述代码转化为:
- objc_msgSend(obj,@selector(makeText));
所以其实
- objc_msgSend(obj,@selector(makeText));
- 和
- <pre name="code" class="plain">[obj makeText];
是等价的。
再比如:
[obj setTT:@"111" isOK:YES];
和
objc_msgSend(obj,@selector(setTT:isOK:),@"111",YES)
也是等价的。注意有参数的OC函数名的表达方式。
首先我们来看看obj这个对象,iOS中的obj都继承与NSObject。
- @interface NSObject{
- Class isa OBJC_ISA_AVAILABILITY
- }
在NSObject中存在一个Class的isa指针。然后我们看看Class是什么东西:这是在objc.h中定义的。
typedef struct objc_class *Class;
- struct objc_class{
- Class isa; //指向metaclass
- Class super_class ;//指向父类
- const char *name;//类名
- long version; //类的版本信息,初始化默认为0,可以通过runtime函数class_setVersion和class_getVersion进行修改读取;
- long info; //一些标识信息,如CLS_CLASS(0x1L)表示该类为普通的class,其中包含对象方法和成员变量;CLS_META(0x2L)表示该类为metaclass,其中包含类方法;
- long instance_size; 该类的实例变量大小(包括从父类继承下来的实例变量);
- struct objc_ivar_list *ivars; //用于存储每个成员变量的地址;
- struct objc_method_list **methodLists; //与info的一些标志位有关,如CLS_CLASS(0x1L),则存储对象方法;
- struct objc_cache *cache; //指向最近使用的方法的指针,用于提升效率;
- struct objc_protocol_list *protocols; //存储该类遵守的协议;
- }
对于一个Class类中,存在很多东西,我们来解释一下重要的东西:
Class isa :指向metaclass ,也就是静态的Class。一般一个Obj对象中的isa会指向普通的Class,这个Class中存储普通成员变量和对象方法(-开头的方法),普通Class中的isa指针指向静态Class,静态Class中存储static类型成员变量和类方法(+开头的方法)。
Class super_class:指向父类,如果这个类是根类,则为NULL。
我们通过下面这个图来了解类和对象的继承关系:
。
注意:所有metaclass中isa指针都指向根metaclass(也就是Root Class Meta),而根metaclass则指向自身。Root metaclass是通过继承Root Class产生的。与root class结构成员一致,也就是前面提到的结构。不同的是Root metaclass的isa指针指向自身。
然后再来看看方法:
@selector(makeText):这是一个SEL方法选择器。SEL的主要作用就是通过方法名字(makeText)查找到对应方法的函数指针,然后调用其函数。SEL其本身是一个int类型的一个地址,地址中存放着方法的名字。对于一个类中,每一个方法对应着一个SEL。所以iOS类中不能存在2个名称相同的方法,即使参数类型不同,因为SEL是根据方法名字生成的,相同的方法名称只能对应一个SEL。同时,我们也应该知道,OC是没有方法重载的。
我们再来看看具体的消息发送之后是怎么样来动态查找对应的方法的。
首先,编译器将代码[obj makeText];转化为objc_msgSend(obj,@selector(makeText));在objc_msgSend函数中,首先通过obj的isa指针找到对应的class。在Class中先去cache中通过SEL查找对应函数method,若cache中未找到,再去methodList中查找,若methodList中未找到,则取superClass中查找。若能找到,则将method加入到cache中,以方便下次查找,并通过method中的函数指针跳转到对应的函数中去执行。
class的方法列表其实是一个字典,key为selector,value为IMP函数指针。一个IMP是指向方法在内存中的实现。很重要的一点,selector和IMP之间的关系是在运行时才决定的,而不是编译时。
对于面向对象而言,万物皆对象,在OC中,类也是对象。The class is Object,也可以处理消息。所以你现在知道为什么会有类方法和实例方法了。
Method Swizzling
我们上面讲过,方法由两个部分组成。selector相当于一个方法的id,IMP是方法的实现。这样分开的一个遍历就是selector和IMP之间的对应关系可以被改变。比如一个IMP可以有多个selectors指向它。
而Method Swizzling可以交换两个方法的实现。在OC中,两种扩展class的途径。首先是subclass.你可以重写某个方法,调用父类的实现,这也意味着你必须使用这个subclass的实例。但是我们如果使用Category分类,重写某个方法之后,就不能再调用原来的方法了。
Method Swizzling可以搞定这个问题,你可以重写某个方法而不用继承,同时还可以调用原先的实现。通常的做法是在Category中添加一个方法,可以通过method_exchangeImplementations这个运行时方法来交换实现。
oc - runtime运行机制的更多相关文章
- IOS runtime运行机制详解(一)
OC运行机制是指,可以运行的时候动态调用函数.因为C语言必须在编译的时候就决定调用哪个函数. 我们平时写的OC代码,它在运行的时候也是转换成了runtime的方式运行的.任何方法调用本质:就是发送一个 ...
- OC的runtime运行机制
什么是runtime runtime就是一套底层的c语言API(Application Programming Interface)里面包括很多强大实用的c语言类型.c语言函数. 实际上,平时我们编写 ...
- runtime 运行机制2
Mike_zh QQ:82643885 end: blogTitle 博客的标题和副标题 博客园 首页 新随笔 联系 订阅 <a id="MyLinks1_XMLLink" ...
- runtime运行机制方法学习
runtime这玩意第一次听说时都不知道是什么,经过了解后才知道它就是oc动态语言的机制,没有它那oc就不能称为动态语言.在之前可能大家对runtime了解都不深,随着编程技能的日益加深和需要,大家开 ...
- runtime 运行机制
// // HKPerson.h // runtimeDemo1 // // Created by 123 on 16/5/23. // Copyright © 2016年 123. All ...
- iOS开发——高级技术OC篇&运行时(Runtime)机制
运行时(Runtime)机制 本文将会以笔者个人的小小研究为例总结一下关于iOS开发中运行时的使用和常用方法的介绍,关于跟多运行时相关技术请查看笔者之前写的运行时高级用法及相关语法或者查看响应官方文档 ...
- iOS-浅谈runtime运行时机制-runtime简单使用(转)
转自http://www.cnblogs.com/guoxiao/p/3583432.html 由于OC是运行时语言,只有在程序运行时,才会去确定对象的类型,并调用类与对象相应的方法.利用runtim ...
- runtime 运行时机制 完全解读
runtime 运行时机制 完全解读 目录[-] import import 我们前面已经讲过一篇runtime 原理,现在这篇文章主要介绍的是runtime是什么以及怎么用!希望对读者有所帮助! ...
- iOS-浅谈runtime运行时机制02-runtime简单使用
http://blog.csdn.net/jiajiayouba/article/details/44201079 由于OC是运行时语言,只有在程序运行时,才会去确定对象的类型,并调用类与对象相应的方 ...
随机推荐
- innodB的隐式锁
http://blog.csdn.net/taozhi20084525/article/details/19545231 一.知识准备之隐式锁 参考:http://www.uml.org.cn/sjj ...
- cocos2dx-lua使用UIListView制作二级折叠菜单
折叠菜单,用过jquery accordion的同学都知道是啥玩艺儿~,图片效果就是介样: cocos2dx不带有此控件,因此我们动手来实现一个. 原理很简单,展开的时候往listview里inser ...
- 将一副图片编译进uboot
在uboot显示图片的时候可以将jpg图片作为uboot的一段,在程序中访问该段,实现图片. 图片: logo.jpg ,将其拷贝到common下 修改u-boot.lds,添加".log& ...
- 手动实现 KVO
来源:伯乐在线 - Jerry4me 链接:http://ios.jobbole.com/88828/ 点击 → 申请加入伯乐在线专栏作者 我的Github地址 : https://github.co ...
- 函数查询(Function Query)
函数查询 可以利用 numeric字段的值 或者 与字段相关的的某个特定的值的函数,来对文档进行评分. 1. 使用函数查询的方法 这里主要有三种方法可以使用函数查询,这三种s方法都是通过solr ...
- entity framework 连接 oracle 发布后出现的问题(Unable to find the requested .Net Framework Data Provider)
用entity framework 搭建的一个windows 程序,在vs中用oracle 的ODT 工具连接oracle数据库,昨天发布后出现下面一个错误, System.ArgumentExcep ...
- IOS 沙盒机制 && 关于document\library\tmp的灵活使用
默认情况下,每个沙盒含有3个文件夹:Documents, Library 和 tmp.因为应用的沙盒机制,应用只能在几个目录下读写文件Documents:苹果建议将程序中建立的或在程序中浏览到的文件数 ...
- SQL 必知必会-- 第17课:创建和操作表
我这里用的是oracle 10g,PL/SQL来做的. 第17课 创建和操纵表 14517.1 创建表 14517.2 更新表 15017.3 删除表 15317.4 重命名表 1 ...
- How to make remote key fob for 2002 BMW 3 series
Here share with you on how to make remote key fob for 2002 BMW 3 series: Method 1: 1. Working within ...
- Android(java)学习笔记67:多线程程序练习
需求: 某电影院目前正在上映贺岁大片,共有100张票,而它有3个售票窗口售票,请设计一个程序模拟该电影院售票. 两种方式实现 A:继承Thread类 B:实现Runnable接 1. 首先我们利用方式 ...