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

转载于:https://www.cnblogs.com/OIMM/p/4938968.html

oc - runtime运行机制相关推荐

  1. ios runtime重要性_iOS 之runtime运行机制理解

    Runtime 是想要做好iOS 开发,或者说是真正的深刻的掌握OC这门语言所必须的理解的东西,最近在学习Runtime. 查阅方便 或许能给他人一些一些启发 什么是Runtime 我们写的代码在程序 ...

  2. 浅谈runtime运行时机制

    由于OC是运行时语言,只有在程序运行时,才会去确定对象的类型,并调用类与对象相应的方法.利用runtime机制让我们可以在程序运行时动态修改类.对象中的所有属性.方法. 下面就介绍运行时一种很简单的使 ...

  3. iOS的runtime运行时机制

    本文转自http://www.cnblogs.com/guoxiao/p/3583432.html 最近一直在研究runtime运行时机制的问题,我想可能也有很多人不太清楚这个问题吧?在这里跟大家沟通 ...

  4. 编译器设计-RunTime运行时环境

    编译器设计-RunTime运行时环境 Compiler Design - Run-Time Environment 作为源代码的程序仅仅是文本(代码.语句等)的集合,要使其活动,它需要在目标计算机上执 ...

  5. 1.java注释的类型_HappyBKs教你写Java注解(1)——注解的分类、运行机制、作用域及概念汇总...

    注解这东西,已经在我们的编程生活中习以为常了.覆盖一个父类的方法,套用Spring.Mybatis中的编程套路,编写JUnit测试函数等等.你会发现,作为一个Java Coder,你无时无刻不在接触它 ...

  6. Apache运行机制剖析

    Apache运行机制剖析: 1. B/S交互过程 浏览器(Browser)和服务器(Web Server)的交互过程: 1.  浏览器向服务器发出HTTP请求(Request). 2.  服务器收到浏 ...

  7. 探索 OSGi 框架的组件运行机制

    在目前的 Java 开发平台中,对于组件开发过程,比如打包.部署和验证等,并没有一个统一的标准.正因如此,许多 Java 项目,例如 JBoss 和 Net Beans,都拥有一套自定义的组件开发规范 ...

  8. JDK,JRE和JVM三者的关系以及java的运行机制,环境变量,三大版本,特点

    JDK,JRE和JVM 1.JVM Java Virtual Machine是Java虚拟机,Java程序需要运行在虚拟机上,不同的平台有自己的虚拟机,因此Java语言可以实现跨平台. 所谓跨平台性, ...

  9. 探究php底层运行机制

    本文转载自:http://www.myext.cn/Article/921.html 概要 简介 先看看下面这个过程: 我们从未手动开启过PHP的相关进程,它是随着Apache的启动而运行的:  ph ...

最新文章

  1. 双列集合,往treeMap里添加元素的时候注意的事项
  2. 用区块链变革教育行业?全球首个教育+旅行+区块链平台——Ambertime:让每个人都能够将时间凝结成自己专属的“琥珀”...
  3. 文件编码方式批量转换工具
  4. 采样算法哪家强?一个针对主流采样算法的比较
  5. 使用 Debian 从 0 开始搭建 hexo 博客
  6. 千亿级市场赛道,阿里云视频云拿下 “三连冠”
  7. 浮动5-常用列表显示(案例)
  8. 简明java_简明 Java 错误处理机制
  9. python是什么课程-请问自学 Python 有必要买课程吗?
  10. python安装配置教程win10_Python 环境安装教程(Windows 10)
  11. cannot be cast to com.activiti.common.config.ICustomProcessDiagramGenerator
  12. VDI、IDV、VOI、RDS四种类型云桌面的区别
  13. flowchart流程图编程语言下载_flowchart.net
  14. win8.1怎样打开计算机名,Win8怎么打开cmd命令窗口_Win8.1打开命令提示符的方法-192路由网...
  15. 操作json进行分组再组
  16. coursera课程下载方法
  17. 基于AForge的C#摄像头视频录制
  18. 学生选课系统-学年学期选择器,根据入学年份自动计算当前学期
  19. crm如何做好客户关系管理?
  20. 又是一年将尽时 移动开始话费大促销

热门文章

  1. 抑制过拟合的方法之Dropout(随机删除神经元)
  2. 数据结构:队列的了解与示例(CPU处理任务的时间)
  3. 初探ECS-Linux,后期还会更新。
  4. 判断点是否在多边形内
  5. 大数据教程(2.2):Linux系统安装JDK1.7
  6. 内存分配知识:全局,局部,静态变量
  7. 如何导入asl文件?ps制作知识
  8. 迁移Exchange Server 2003
  9. PAT1061. 判断题
  10. Web前端JavaScript笔记(7)ECMA6新增数组方法