iOS编程基础-OC(八)-运行时系统的结构

2023-11-17

该系列文章系个人读书笔记及总结性内容,任何组织和个人不得转载进行商业活动!


第八章 运行时系统的结构

     

     运行时系统是OC平台的关键元素;

        OC语言的动态特性和面向对象功能就是由它实现的;

        运行时系统提供了公用API,是你编写的代码能够直接调用运行时系统服务;

     

     8.1 运行时系统的组成部分

     

     OC运行时系统的两个组成部分:编译器 和 运行时系统库;

     

     8.1.1 编译器

     

     第五章简述了源代码常规编译过程;

        编译进程会接收OC源文件并进行处理(该处理过程由词法分析、语法分析、代码生成和优化、汇编以及链接操作等阶段构成),生成构成可执行程序的二进制文件;

        和C语言标准库会为C语言程序提供标准API和实现代码一样,运行时系统库也会为OC的面向对象特性 提供标准的API和实现代码;

        这种库与所有OC程序链接(在链接阶段);

        编译器的作用是接收输入的源代码,生成使用了运行时系统库的代码,从而得到合法的可执行的OC程序;

     

     OC语言中的面向对象元素和动态特性都是通过运行时系统实现的;

     运行时系统组成部分:

     1)类元素(接口、实现代码、协议、分类、方法、属性、实例变量);

     2)类实例(对象);

     3)对象消息传递(包括动态类型和动态绑定);

     4)动态方法决议;

     5)动态加载;

     6)对象内省;

     

     简单来说,当编译器解析使用了这些语言元素和特性的OC源代码时,就会使用适当的运行时系统库数据结构和实现该语言特定行为的函数,生成可执行代码;

     

     我们来看看编译器如何为OC类和对象生成可执行代码,以及如何实现对象消息;

     

     1.生成对象消息传递代码

     

     当编译器解析对象消息(发送消息的表达式)时,如:

        [接收器 消息]

        它会生成 调用运行时系统库中函数objc_msgSend()的代码;

            该函数将接收器、选择器和消息传递的参数作为输入参数;

            因此,编译器会将源代码中的所有消息传递表达式([接收器 消息]形式的),转换为调用运行时系统库函数objc_msgSend(...)的代码,并为这些调用代码提供源代码所提供的参数;

            每条消息都是以动态的方式处理的,这意味着接收器的类型和方法的实际实现代码都是在运行程序时决定的;

        对于源代码中的类和对象来说,编译器创建了执行对象消息操作所需要的数据结构;

     

     2.生成类和对象的代码

     

     当编译器解析含有类定义和对象的OC源码时;

        它会生成 相应的运行时数据结构;

            OC中的类与运行时系统库中的Class数据结构对应;

            Class数据类型是指向带objc_class标识符的不透明数据类型的指针,如:

                typedef struct objc_class * Class;

     

     不透明数据类型是一种接口定义不完整的C语言结构类型;

        不透明类型提供了一种数据隐藏模式;

        因为其变量只能由专门为它们定义的函数访问;

        使用运行时系统库中的函数可以访问Class(即objc_class)数据类型的变量;

     

     OC类拥有运行时的数据类型,与之类似,OC对象也有;

        编译器在解析OC对象的源代码时,会生成创建运行时对象类型的可执行代码;

        这种数据类型是一种带有objc_object标识符的C语言结构;

     

     可以大致看下objc_object数据类型:

        struct objc_object{

            Class isa;

            '/'*...含有实例变量值的长度可变数据...'*'/

        }

     当你编写的程序创建对象时,系统会为objc_object类型数据分配内存;

     可以看到,这种数据由isa指针后跟实例变量的数据组成;

     

     我们注意到,上述结构中isa是Class结构的;

        像Class数据类型一样,objc_object类型也含有Class类型的isa变量,换言之,该变量就是指向objc_class类型变量的指针;

        事实上,所有OC对象和类的运行时类型都是以isa指针开头的;

     

     与OC中id数据类型对应的运行时数据类型是一种C语言结构,该结构被定义为指向objc_object的指针;

        typedef struct objc_object{

            Class isa;

        } * id;

        也就是说,id就是一个带有objc_object标识符的、指向C语言结构的指针;

        同样,OC块对象也拥有相应的运行时数据结构,因此运行时系统也能够以适当的方式管理他们;

     

     3.查看运行时系统的数据结构

     

     掌握了以上概念,我们来看一个例子;

     新建C8TestClass1类;

     (Code1)

#import <objc/runtime.h>
//
@interface C8TestClass1:NSObject{
    @public
    int myInt;
}
@end

@implementation C8TestClass1

@end

     

     测试代码如下:

    C8TestClass1 * tc1A = [[C8TestClass1 alloc] init];
    tc1A->myInt = 0x5a5a5a;
    
    C8TestClass1 * tc1B = [[C8TestClass1 alloc] init];
    tc1B->myInt = 0xc3c3c3;
    
    long tc1Size = class_getInstanceSize([C8TestClass1 class]);
    NSData * obj1Data = [NSData dataWithBytes:(__bridge const void *)(tc1A) length:tc1Size];
    NSData * obj2Data = [NSData dataWithBytes:(__bridge const void *)(tc1B) length:tc1Size];

    NSLog(@"C8TestClass1 object tc1 contains %@",obj1Data);
    NSLog(@"C8TestClass1 object tc2 contains %@",obj2Data);
    NSLog(@"C8TestClass1 memory address = %p",[C8TestClass1 class]);

     log:

     2017-12-07 16:38:41.888771+0800 精通Objective-C[69375:18798951] C8TestClass1 object tc1 contains <88096101 01000000 5a5a5a00 00000000>

     2017-12-07 16:38:41.888945+0800 精通Objective-C[69375:18798951] C8TestClass1 object tc2 contains <88096101 01000000 c3c3c300 00000000>

     2017-12-07 16:38:41.889044+0800 精通Objective-C[69375:18798951] C8TestClass1 memory address = 0x101610988

     

     分析下这段代码:

     1)导入运行时头文件;

     2)新建测试类,定义了一个全局的实例变量;

     3)使用NSData类获取已经创建对象的数据(以字节为单位);

        这里还是用了运行时系统库函数class_getInstanceSize获取类实例的尺寸(以字节为单位);

     

     运行我们得到了上述log;

     

     编译器解析对象时,就会生成objc_object类型的实例;

        该实例由一个isa指针和对象实例变量的值构成;

        因此上述log中对象tc1包含的两项内容:

            一个是isa指针(88096101 01000000)

            一个是对象实例变量的值(5a5a5a00 00000000)

        同样的对象tc2也包含的两项内容:

            一个是isa指针(88096101 01000000)

            一个是对象实例变量的值(c3c3c300 00000000)

     注意:

        对象objc_object数据结构中的第一项就是其isa指针;

        两个对象的isa指针都是相同的,因为他们是同一个类的实例,因此拥有相同的指针值;

        这个isa指针,指向该类的内存地址;

     

     这里可能会一会我们打印的[C8TestClass1 class]的内存地址是0x101610988,与前面显示的指针值不一样;

        实际是相同的,这个程序是在MacPro上运行的,而这种计算机使用的是低字节序(little-endian),换言之它们会使用翻转的字节顺序存储数据;

        这个0x101610988是以翻转的字节顺序显示的;

我们接着看

@interface C8TestClass2:NSObject{
@public
    int myInt;
}
@end

@implementation C8TestClass2

@end

    id testClz = objc_getClass("C8TestClass1");
    long tcSize = class_getInstanceSize([testClz class]);
    NSData * tcData = [NSData dataWithBytes:(__bridge const void * _Nullable)(testClz) length:tcSize];
    NSLog(@"C8TestClass1 class contains %@",tcData);
    NSLog(@"C8TestClass1 superclass memory address = %p",[C8TestClass1 superclass]);
    
    id testClr = objc_getClass("C8TestClass2");
    long tcrSize = class_getInstanceSize([testClr class]);
    NSData * tcrData = [NSData dataWithBytes:(__bridge const void * _Nullable)(testClr) length:tcrSize];
    NSLog(@"C8TestClass2 class contains %@",tcrData);
    NSLog(@"C8TestClass2 superclass memory address = %p",[C8TestClass2 superclass]);
    


      log:

      2017-12-07 17:59:13.679936+0800 精通Objective-C[70080:18859295] C8TestClass1 object tc1 contains <e87a9a0c 01000000 5a5a5a00 00000000>

      2017-12-07 17:59:13.680099+0800 精通Objective-C[70080:18859295] C8TestClass1 object tc2 contains <e87a9a0c 01000000 c3c3c300 00000000>

      2017-12-07 17:59:13.680194+0800 精通Objective-C[70080:18859295] C8TestClass1 memory address = 0x10c9a7ae8

      

      2017-12-07 17:59:13.680303+0800 精通Objective-C[70080:18859295] C8TestClass1 class contains <c07a9a0c 01000000 a81e960d 01000000>

      2017-12-07 17:59:13.680387+0800 精通Objective-C[70080:18859295] C8TestClass1 superclass memory address = 0x10d961ea8

      2017-12-07 17:59:13.680515+0800 精通Objective-C[70080:18859295] C8TestClass2 class contains <107b9a0c 01000000 a81e960d 01000000>

      2017-12-07 17:59:13.680631+0800 精通Objective-C[70080:18859295] C8TestClass2 superclass memory address = 0x10d961ea8

      

      再来看一下这段log:

        前三个打印还是之前对象包含的isa指针和实例变量的值;

        C8TestClass1类含有的数据是一个isa指针(c07a9a0c 01000000)和另一个值(a81e960d 01000000);

        这个值实际上是指向该类的父类的指针;

      

      我们之前介绍过:

        Class数据类型和objc_object类型都含有Class类型的isa变量,这里也可以看到类的数据结构确实拥有isa指针;

        这里我又新建了一个类C8TestClass2,做了同样的操作,由于和C8TestClass1都继承了NSObject类,所以可以看到打印类中含有的后一个数据是相同的,都是指向父类的指针;

      

      这里大家可能会有一个疑问:

        对象的isa指针是指向类的地址,那么类的isa指针指向的又是什么呢?

        我们先留下这个问题,一会会解答;

      

      现在我们已经了解到了编译器在运行时系统中的作用,使用运行时系统API检查程序来观察编译器生成的数据结构;

      接下来再看看运行时系统库及其实现细节,相信我,你会学到很多!

      


本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

iOS编程基础-OC(八)-运行时系统的结构 的相关文章

随机推荐

  • 02. MAC地址 · ARP · ICMP · IP地址 · 子网 · 超网 · 端口

    MAC IP 子网 超网 MAC地址 MAC地址的表示格式 MAC地址操作 查看 修改 实践 MAC地址的获取 实践 ARP ICMP IP地址 子网掩码 IP地址的组成 IP地址的分类 A类地址 B类地址 C类地址 D类地址 E类地址 子
  • ElasticSearch的可视化应用 ElasticHD

    ElasticHD 详细介绍 ElasticHD 是一款 ElasticSearch的可视化应用 不依赖ES的插件安装 更便捷 导航栏直接填写对应的ES IP和端口就可以操作Es了 目前支持如下功能 ES Real time data se
  • curl命令怎么在没有token的情况下访问https请求-ChatGPT回答

    curl命令怎么在没有token的情况下访问https请求 ChatGPT回答 你可以使用curl的 k选项来忽略服务器证书的验证 这样就可以在不使用任何认证令牌的情况下访问https请求了 例如 curl k https example
  • UNIX网络编程读书笔记(四)第四章 基本TCP套接字编程

    文章目录 概述 socket函数 connect函数 bind函数 listen函数 accept函数 fork和exec函数 并发服务器 close函数 描述引用计数 getsockname和getpeername函数 小结 概述 TCP
  • JDBC 获取数据库连接的方式一,二

    获取方式一 通过实现Driver类的对象获取 使用此方式需要实现以下步骤 1 获取Driver实现类的对象 2 将用户名和密码信息封装在Properties中 3 获取连接 这样我们就获取到了java与数据库的连接 JDBC URL用于标识
  • 订阅消息发送47003

    订阅消息发送失败信息 errcode 47003 errmsg argument invalid data phrase4 value invalid rid 600a44c7 56086c1c 4f499b49 提示phrase4这个字段
  • Cypress笔记--隐式断言.should()和.and()方法详解

    should 作用 为当前对象做断言 语法 should 方法 预期 示例 cy get assertion table find tbody tr last should have class success find td first
  • vue3 无法导入vue-router 报错vue_router__WEBPACK_IMPORTED_MODULE_1__.default is undefined

    问题描述 报错vue router WEBPACK IMPORTED MODULE 1 default is undefined 在启动的时候 报错 export default imported as Vue was not found
  • 蓝图类

    感谢程序员的暴击 https www bilibili com video BV125411h7c4 p 17 这个例子说明了蓝图类的用法 而不是关卡蓝图 把蓝图类当作类就可以了 可以把蓝图类拖到场景多份 就像多个对象一样 蓝图类更加灵活
  • oracle和mysql的区别

    Oracle与MySQL的区别以及优缺点 MySQL的特点 1 性能卓越 服务稳定 很少出现异常宕机 2 开放源代码无版本制约 自主性及使用成本低 3 历史悠久 社区和用户非常活跃 遇到问题及时寻求帮助 4 软件体积小 安装使用简单且易于维
  • tp5 ueditor 请求后台配置项http错误,上传功能将不能正常使用 报错403解决

    百度的都尝试过了 对我没有用 时间有限 简单改了一下部分功能 大部分功能是可以用的 我重新改了一下 打开ueditor本目录下的ueditor confin js将serverUrl修改为您的上传接口 大约在33行左右 serverUrl
  • 【产品运营】如何提升B端产品竞争力(下)

    好产品不是能力内核 做好产品的流程才是 一 建立需求池和需求反馈渠道 需求池管理是B端产品进化最重要的环节 它的重要性远超产品设计 开发等其他环节 维护需求池有主动和被动两种 主动维护是产品经理在参与售前 迭代 交付 售后 竞品分析 老板沟
  • dell 台式机bios虚拟化_如何在BIOS中开启虚拟化技术

    虚拟化技术目前主要依赖于您电脑的CPU型号及BIOS 某些CPU或者BIOS暂时还不能支持虚拟化技术 支持虚拟化技术的可以在BIOS中开启 开启方法如下 1 进入BIOS 开机时按F2或F12或DEL或ESC等键 各电脑有所不同 2 进入B
  • 一些有意思的面试题

    1 写一个高效C语言程序 计算一个无符号整数中1的个数 for count 0 x count x x 1 同理 计算0的位数 for count 32 x count x x 1 2 给定字符串S1和S2 写程序判断S2是否能由S1旋转而
  • 局域网中共享文件夹

    以 Windows 10 系统为例 首先 在右下角的网络图标上单击右键 选择 打开 网络和Internet 设置 然后点击这里的 网络和共享中心 在弹出的窗口中 点击 更改高级共享设置 在来宾或公用网络这里 勾选 启用网络发现 勾选 启用文
  • R语言第三章练习题

    R语言第三章练习题 1 求10以内所有偶数的和 a lt 0 cou lt 0 while a lt 9 a a 1 if a 2 0 cou lt cou a print cou 2 求焉尾花数据集iris属性的均值 中位数 至少用三种方
  • Vue的指令(一)

    指令 Directives 是vue为开发者提供的模板语法 用于辅助开发者渲染页面的基本结构 Vue中指令按照不同的用途可分为6大类 1 内容渲染指令 内容渲染指令用来辅助开发者渲染DOM元素的文本内容 常用的内容渲染指令有3个 v tex
  • Java入门实例(九九乘法表)

    乘法口诀 也叫 九九歌 在我国很早就已产生 远在春秋战国时代 九九歌就已经广泛地被人们利用着 在当时的许多著作中 已经引用部分乘法口诀 最初的九九歌是以 九九八十一 起到 二二如四 止 共36句口诀 发掘出的汉朝 竹木简 以及敦煌发现的古
  • docker(5)-数据卷

    容器运行时会产生一些数据 在容器内部不便于管理 而且容器删除后数据也会被删除 数据卷可以将容器中的动态数据直接存储到宿主机上 独立于容器 挂载自定义目录 docker run id v root data1 root openjdk 8 v
  • iOS编程基础-OC(八)-运行时系统的结构

    该系列文章系个人读书笔记及总结性内容 任何组织和个人不得转载进行商业活动 第八章 运行时系统的结构 运行时系统是OC平台的关键元素 OC语言的动态特性和面向对象功能就是由它实现的 运行时系统提供了公用API 是你编写的代码能够直接调用运行时