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中写了如下代码:

[plain] view plaincopyprint?

  1. [obj makeText];

其中obj是一个对象,makeText是一个函数名称。执行一个方法,有些语言,编译器会执行一些额外的优化和错误检查,因为调用关系很直接也很明显。但是对于消息分发来说,就不那么明显了,在发消息之前不必知道某个对象是否能够处理消息。你把消息发给他,他可能会处理,也可能转给其他的Object来处理。一个消息不必对应一个方法,一个对象可能实现一个方法来处理多条消息。

在编译时RunTime会将上述代码转化为:

  1. objc_msgSend(obj,@selector(makeText));

所以其实

  1. objc_msgSend(obj,@selector(makeText));
  2. <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。

  1. @interface NSObject{
  2. Class isa OBJC_ISA_AVAILABILITY
  3. }

在NSObject中存在一个Class的isa指针。然后我们看看Class是什么东西:这是在objc.h中定义的。

  typedef struct objc_class *Class;

  1. struct objc_class{
  2. Class isa; //指向metaclass
  3. Class super_class ;//指向父类
  4. const char *name;//类名
  5. long version;  //类的版本信息,初始化默认为0,可以通过runtime函数class_setVersion和class_getVersion进行修改读取;
  6. long info;   //一些标识信息,如CLS_CLASS(0x1L)表示该类为普通的class,其中包含对象方法和成员变量;CLS_META(0x2L)表示该类为metaclass,其中包含类方法;
  7. long instance_size;  该类的实例变量大小(包括从父类继承下来的实例变量);
  8. struct objc_ivar_list *ivars;  //用于存储每个成员变量的地址;
  9. struct objc_method_list **methodLists;   //与info的一些标志位有关,如CLS_CLASS(0x1L),则存储对象方法;
  10. struct objc_cache *cache;  //指向最近使用的方法的指针,用于提升效率;
  11. struct objc_protocol_list *protocols;  //存储该类遵守的协议;
  12. }

对于一个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这个运行时方法来交换实现。

时间: 2024-10-11 11:37:41

oc - runtime运行机制的相关文章

OC的runtime运行机制

什么是runtime runtime就是一套底层的c语言API(Application Programming Interface)里面包括很多强大实用的c语言类型.c语言函数. 实际上,平时我们编写的oc代码,底层都是基于runtime实现的. 也就是说我们平时编写的oc代码最终都是转成了底层的runtime代码(c语言代码) . [obj say] —> 调用id objc_msgSend(id self, SEL op, ...)函数 -> 里面的obj.isa指针-> class

runtime运行机制

这篇文章主要介绍的是runtime是什么以及怎么用!希望对读者有所帮助! 第一个问题, 1>runtime实现的机制是什么,怎么用,一般用于干嘛? runtime是一套比较底层的纯C语言API, 属于1个C语言库, 包含了很多底层的C语言API. 在我们平时编写的OC代码中, 程序运行过程时, 其实最终都是转成了runtime的C语言代码, runtime算是OC的幕后工作者 比如说,下面一个创建对象的方法中, 对比举例: OC : [[MJPerson alloc] init] runtime

runtime运行机制方法学习

runtime这玩意第一次听说时都不知道是什么,经过了解后才知道它就是oc动态语言的机制,没有它那oc就不能称为动态语言.在之前可能大家对runtime了解都不深,随着编程技能的日益加深和需要,大家开始更加关心底层的实现,并用自己更需要的方式实现.这时runtime开始慢慢火起来了,作为一个iOS程序员,如果出去说自己不知道runtime无疑是一件很丢分的事情.由于runtime的底层实现笔者也没搞得太明白,在这里就跟大家提提几个关于runtime的方法. 1.runtime之动态添加属性 在最

Runtime运行时机制原理、用途

首先我们先了解一下runtime实现机制及作用 1.runtime实现的机制是什么? runtime是一套比较底层的纯C语言API, 属于1个C语言库, 包含了很多底层的C语言API. 在我们平时编写的OC代码中, 程序运行过程时, 其实最终都是转成了runtime的C语言代码, runtime算是OC的幕后工作者比如说,下面一个创建对象的方法中, 举例: OC : [[MJPerson alloc] init] runtime : objc_msgSend(objc_msgSend(“MJPe

Runtime运行时机制

p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Menlo; color: #78492a } p.p2 { margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px "PingFang SC"; color: #000000 } p.p3 { margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px "PingFang SC"; color

runtime 运行时机制 完全解读

runtime 运行时机制 完全解读 目录[-] import import 我们前面已经讲过一篇runtime 原理,现在这篇文章主要介绍的是runtime是什么以及怎么用!希望对读者有所帮助! 首先,第一个问题, 1>runtime实现的机制是什么,怎么用,一般用于干嘛? 这个问题我就不跟大家绕弯子了,直接告诉大家, runtime是一套比较底层的纯C语言API, 属于1个C语言库, 包含了很多底层的C语言API. 在我们平时编写的OC代码中, 程序运行过程时, 其实最终都是转成了runti

?runtime 运行时机制

?runtime 运行时机制 完全解读 摘要 在最开始听到runtime的时候,我是感到恐惧的,多么高大上的东西啊!!!后来,开始在网上查一些资料,可是就是只有那么几篇,看了好久,还不知所云,所以就更加恐惧了!!!!后来经过查看documents 以及一些国外大牛的blogs,终于对runtime有了更深刻的了解!于是就想写下这些东西,希望对读者们有帮助…… runtime 运行时机制 详细解读 目录[-] import import 我们前面已经讲过一篇runtime 原理,现在这篇文章主要介

iOS开发——高级技术OC篇&amp;运行时(Runtime)机制

运行时(Runtime)机制 本文将会以笔者个人的小小研究为例总结一下关于iOS开发中运行时的使用和常用方法的介绍,关于跟多运行时相关技术请查看笔者之前写的运行时高级用法及相关语法或者查看响应官方文档. 下面就来看看什么是运行时,我们要怎么在iOS开发中去使用它. 官方介绍: 这里我们主要关注的是最后一种! 下面来看看Runtime的相关总结 #pragma mark 获取属性成员 /********************************************************

iOS-浅谈runtime运行时机制02-runtime简单使用

http://blog.csdn.net/jiajiayouba/article/details/44201079 由于OC是运行时语言,只有在程序运行时,才会去确定对象的类型,并调用类与对象相应的方法.利用runtime机制让我们可以在程序运行时动态修改类.对象中的所有属性.方法. 下面就介绍运行时一种很简单的使用方式,将字典对象转为模型.当然,你可能会问,我用KVO直接调用 setValuesForKeysWithDictionary:方法,传入一个字典一样可以快速将字典转模型啊,但是这种方