阅读 294

iOS底层之Runtime运行时&方法本质探索

前言

作为一个iOS开发人员,我们都知道OC语言是一个动态运行时的语言,底层基于Runtime机制,那么什么是运行时呢?他在我们代码运行的时候起到了什么作用呢?另外,在我们的代码中调用了一份方法,那么在Runtime机制下,这个方法时如何调用的呢?今天我们一块儿来探索一下

一:什么是Runtime

运行时,就是代码跑起来来,被装载到内存中去了,只有代码被装载到内存中以后才能被系统调用,才能变成活的,在代码只保存在磁盘上而没有被装载到内存之前系统是无法识别的; 在这几年发展中产生了两个Runtime的版本,一个是Legacy版本(Objective-C 1.0),这个版本是早起版本,主要运行在Objective-C 1.0,32位的Mac OS X的平台上,另一个是Modern版本(Objective-C 2.0),是我们现在正在使用的版本,主要应用在iPhone 程序和Mac OS X v10.5及以后的系统中的64位程序中;

那么在程序运行当中Runtime是如何发起的呢?现在有3中方式可以调起Runtime;

1.从OC层面直接调起方法

2.通过NSObject这一层调起api

3.通过objc下层的api

image.gif

从这张图片上我们可以大概了解到我们代码到运行起来以后的层级结构

image.gif

这张图片体现了Runtime调起的三成不同的方式 [person sayNB];是从OC层面直接调起方法; isKindofClass;是通过NSObject这一层调起api; Class_getInstanceSize;是通过objc下层的api;

二 通过代码验证Runtime

如下图我们创建了一个CYFPerson类,并在这个类中声明了两个方法,sayNB和sayHello。

image.gif

其中sayNB已经实现,而sayHello只声明了,并没有去实现;

image.gif

这时候,我们通过command+B,对项目进行编译一下,我们发现可以编译成功,但是当我们command+R把项目运行起来的时候,发现程序crash了,并且报了unrecognized selector 错误;

image.gif

因此,从上边的流程我们可以看出,这个错误是发生在了运行时,而不是编译时,这就是运行时和编译时的区别;

三 系统调用方法的时候,底层做了什么?

在我们的开发过程中,我们经常会调用自己写的函数方法,当我们执行了[person sayNB];这行代码以后,Runtime帮我们做了什么呢?我们通过Clang -rewrite-objc main.m -o -main.cpp 命令查看转换后的代码如下图:

image.gif

从图中我们可以看出来,在我们调用方法以后Runtime机制将我们的代码转换成了objec_msgSend(消息的接收者,消息的主体(sel+参数))的形式;

那么这时候我们是否会有这种想法呢,我们如果直接调用系统的api,objc_msgSend(person, @selector(sayNB));是否可以达到我们想要的结果呢,经过测试,是完全可以的,因此我们可以得到“调用方法=消息发送”这个结论了;

但是有一种情况,当我们调用的方法在子类中不存在,但是存在于父类中的时候,系统是如何处理的呢,经过探究我们可以知道,在Runtime中存在objc_msgSendSuper(),这个接口我们可以通过一下方式调用,这时候我们便可以在子类中访问父类的方法了;

image.gif

总结

由上边流程我们可以的出,方法的本质,是消息发送;

文章分类
iOS
文章标签