zoukankan      html  css  js  c++  java
  • iOS中Runtime理论知识过一遍

    一、Runtime简介

    Runtime其实就是一套API,是一套由C、C++、汇编语言一起写成的API,给OC提供运行时。

    Runtime是OC的底层,采用的是C、C++、汇编语言为OC语言提供运行时环境的支持。

    Runtime System Library是用C、C++、汇编语言写的一个代码库,通过编译(Compiler)之后,生成的就是Runtime API和框架与服务,然后再供OC代码调用。

    不管怎么说,关于Runtime的两篇官方文章是需要自己学习的:

    1》Objective-C Runtime Programming Guide(传送门

    2》Objective-C Runtime Reference(传送门

    与运行时相对应的是编译时,编译时就是编译的时期,编译时主要的任务就是将源代码翻译一下,就是将高级语言(比如OC、Swift、Java等)编译成相应的机器语言,最终形成二进制可执行文件。然后进入到运行时,代码跑起来会被装载到内存中,这个时候代码就变“活”了,这个时候就需要一种东西来支持运行时需要的功能,这个东西就是runtime。

    Runtime发展以来,一共有两个版本,一个是Legacy版本(早期版本),另一个是Modern版本(现行版本)。早期版本对应的编程接口是OC 1.0 ,现行版本对应的编程接口是OC 2.0 。早期版本用于OC 1.0,32位的Mac OS X 的平台上,现行版本用于iPhone程序和Mac OS X v10.5 及以后的系统中的64位程序。

    OC程序有三种途径和运行时系统交互:1)通过OC源代码。2)通过Foundation框架中NSObject的方法。3)通过调用运行时系统给我们提供的API接口。

    通过在项目运行时打断点的方式是无法调到runtime实现方法中去的,如果想跟踪runtime中方法的跳转,需要在Apple Open Source下载runtime的源码到项目中。本文使用的是10.14系统中的objc4-750版本的源代码。然后也可以去github上找相关的源代码(注释版)。

    关于运行时一个很简单的认识就是,在代码中调用[obj run]时,如果run方法只是声明了,但是没实现的情况下。在command+B(编译)的时候并不会出现错误,只有在command+R(运行)的时候才会显示出错误。也就是说,方法的检测是在运行期进行的。

    通过clang可以直接对OC文件进行编译,首先要cd到该OC源文件的所在文件件,比如这个OC源文件是main.m,输入的终端命令行为;

    clang -rewrite-objc main.m -o main.cpp

    编译之后会发现有很多的警告,通过下面这种方式编译,警告会少很多:

    xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o testMain.c++

     通过对一个简单的OC代码编译为c++代码:

    Person *person = [[Person alloc] init];

    [person run];

    编译为C++后为:

    Person *person = ((Person *(*)(id, SEL))(void *)objc_msgSend)((id)((Person *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("Person"), sel_registerName("alloc")), sel_registerName("init"));
            
    ((void (*)(id, SEL))(void *)objc_msgSend)((id)person, sel_registerName("run"));

    由此可知,1)在OC中任何方法的本质就是发送消息objc_msgSend。2)通过下面截图中显示的Person的定义,可知OC对象的本质就是结构体。

     

    然后消息的组成是:消息接受者、方法编号。

    消息的接受者就是:

    ((void (*)(id, SEL))(void *)objc_msgSend)((id)person

    方法编号:

    sel_registerName("run"))

    也就是说,

     person对象调用run方法的模型模型就是:

    void runIMP(id self, SEL_cmd) {
    
    }

    IMP值的是函数实现的指针,IMP是通过sel找到的。

    上面的

    sel_registerName("run")

    或者看起来比较陌生,其实它和@selector(run)是一个意思。

    上面提到的是向对象发送消息,那么类方法是怎样的编译底层呢,向父类发送实例方法消息和向父类发送类方法消息又是怎样的呢?

    // 类方法编译底层
    id cls = [Person class];
    void *pointA = &cls;
    [(__bridge id)pointA walk];
    objc_msgSend(objc_getClass("Person"), sel_registerName("run"));

    // 向父类发消息(对象方法)
    struct objc_super mySuper;
    mySuper.receiver = s;
    mySuper.super_class = class_getSuperclass([s class]);
    objc_msgSendSuper(&mySuper, @selector(run));

    // 向父类发消息(类方法)
    struct objc_super myClassSuper;
    myClassSuper.receiver = [s class];
    myClassSuper.super_class = class_getSuperclass(object_getClass([s class]));
    objc_msgSendSuper(&myClassSuper, sel_registerName("run"));

    objc_msgSend发送消息有两种方式,一种是快速的方式,实现的方式是用缓存中的汇编cache_t,找相应的IMP哈希表,如果没有找到就通过另外一个复杂的过程,也就是第二种方式,慢速的方式,通过C语言慢慢地找,找到后会把这个IMP放到缓存中,

    -----未完待续,2021年6月14日

  • 相关阅读:
    k-近邻算法(kNN)完整代码
    k-近邻算法(kNN)测试算法:作为完整程序验证分类器
    kNN#约会网站预测数据
    k-近邻算法(kNN)准备数据:归一化数值
    高并发编程陷阱之check and set
    functional javascript
    test markdown
    【动态规划】---电路布线
    第一个wxWidgets程序
    深入理解计算机系统-第一章
  • 原文地址:https://www.cnblogs.com/cchHers/p/14882247.html
Copyright © 2011-2022 走看看