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

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

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

  1. IOS runtime运行机制详解(一)

    OC运行机制是指,可以运行的时候动态调用函数.因为C语言必须在编译的时候就决定调用哪个函数. 我们平时写的OC代码,它在运行的时候也是转换成了runtime的方式运行的.任何方法调用本质:就是发送一个 ...

  2. OC的runtime运行机制

    什么是runtime runtime就是一套底层的c语言API(Application Programming Interface)里面包括很多强大实用的c语言类型.c语言函数. 实际上,平时我们编写 ...

  3. runtime 运行机制2

    Mike_zh QQ:82643885 end: blogTitle 博客的标题和副标题 博客园 首页 新随笔 联系 订阅 <a id="MyLinks1_XMLLink" ...

  4. runtime运行机制方法学习

    runtime这玩意第一次听说时都不知道是什么,经过了解后才知道它就是oc动态语言的机制,没有它那oc就不能称为动态语言.在之前可能大家对runtime了解都不深,随着编程技能的日益加深和需要,大家开 ...

  5. runtime 运行机制

    // //  HKPerson.h //  runtimeDemo1 // //  Created by 123 on 16/5/23. //  Copyright © 2016年 123. All ...

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

    运行时(Runtime)机制 本文将会以笔者个人的小小研究为例总结一下关于iOS开发中运行时的使用和常用方法的介绍,关于跟多运行时相关技术请查看笔者之前写的运行时高级用法及相关语法或者查看响应官方文档 ...

  7. iOS-浅谈runtime运行时机制-runtime简单使用(转)

    转自http://www.cnblogs.com/guoxiao/p/3583432.html 由于OC是运行时语言,只有在程序运行时,才会去确定对象的类型,并调用类与对象相应的方法.利用runtim ...

  8. runtime 运行时机制 完全解读

    runtime 运行时机制 完全解读   目录[-] import import 我们前面已经讲过一篇runtime 原理,现在这篇文章主要介绍的是runtime是什么以及怎么用!希望对读者有所帮助! ...

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

    http://blog.csdn.net/jiajiayouba/article/details/44201079 由于OC是运行时语言,只有在程序运行时,才会去确定对象的类型,并调用类与对象相应的方 ...

随机推荐

  1. 你所不知到的C++ 系列

    http://blog.csdn.net/doon/article/category/2926337

  2. mysql分表研究

    分表是分散数据库压力的好方法. 分表,最直白的意思,就是将一个表结构分为多个表,然后,可以再同一个库里,也可以放到不同的库. 当然,首先要知道什么情况下,才需要分表.个人觉得单表记录条数达到百万到千万 ...

  3. Windows下memcache安装使用

    Windows下Memcache安装 随着时间的推移,网上现在能找到的在 Windows下安装 Memcache 的文档大多已经过时.雪峰这里再简要介绍一下当下最新版的安装和配置方法. Memcach ...

  4. B. Pasha and String

    time limit per test 2 seconds memory limit per test 256 megabytes input standard input output standa ...

  5. Sum of divisors

    Problem Description mmm is learning division, she's so proud of herself that she can figure out the ...

  6. setsockopt

    1.closesocket(一般不会立即关闭而经历TIME_WAIT的过程)后想继续重用该socket:BOOL bReuseaddr=TRUE;setsockopt(s,SOL_SOCKET ,SO ...

  7. 【阿里云产品公测】阿里云ACE配置全程图解,详细到不行!

    作者:阿里云用户sofia 看过阿里云社区的其他技术大姥们的评测教程,感觉还是不够详细,对于一个第一次接触ace.新浪sae这类的应用来说还是比较陌生的.我最喜欢写教程了,不过我有我的风格,那就是简单 ...

  8. Redis操作的封装类

    using System; using System.Collections.Generic; using System.Linq; using System.Text; using ServiceS ...

  9. C如何使用内存

    栈:   自动变量:auto.变量的地址在栈中.   C语言函数调用的实现: 在调用方,参数从后往前按顺序被堆积在栈中 和函数调用关联的返回信息(返回地址等)也被堆积在栈中. 一旦函数调用结束,局部变 ...

  10. Operation与GCD的不同

    最大并发数: 什么是并发数? 同时执行的任务数.比如同时开启三个线程执行三个任务,并发数就是3. 最大并发数相关的方法: -(NSInteger)maxConcurrentOperationCount ...