「iOS」————继承链与对象的结构

iOS学习

  • 前言
  • 对象的底层结构
    • isa的类型isa_t
    • objc_class & objc_object
      • 类信息的静态与动态存储(ro、rw、rwe机制)
      • cache
      • bits
  • 继承链
  • isKindOfClass和isMemberOfClass
      • `isKindOfClass:`
      • `isMemberofClass`


前言

对 对象底层结构的相关信息有点遗忘,简略记录一下。顺便记录一下继承链相关。

对象的底层结构

isa指针采用了典型的适配器设计模式。
适配器设计模式(即将底层接口适配为客户端需要的接口),达成上下层接口隔离的目的。

  • 上层OC对象 只需要通过 isa、Class、objc_object、objc_class 等接口访问对象和类信息。 底层实现
  • 通过联合体、结构体、位域等方式,封装了复杂的内存布局和数据结构。
  • 适配器(如isa_t、objc_object、objc_class、cache_t、class_rw_t等)把底层复杂的数据结构和操作,适配成了上层易用的接口,实现了“接口隔离”和“解耦”。

isa的类型isa_t

union isa_t { //联合体isa_t() { }isa_t(uintptr_t value) : bits(value) { }//提供了cls 和 bits ,两者是互斥关系,即通过cls初始化,bits无默认值,通过bits初始化,cls无默认值Class cls;uintptr_t bits;
#if defined(ISA_BITFIELD)struct {ISA_BITFIELD;  // defined in isa.h  结构体定义的位域,储存类信息和其他信息。};
#endif
};

位域如下:

isa存储情况

  • nonpointer有两个值,表示自定义的类等,占1
    • 0纯isa指针
    • 1:不只是类对象地址,isa中包含了类信息、对象的引用计数
  • has_assoc表示关联对象标志位,占1
    • 0没有关联对象
    • 1存在关联对象
  • has_cxx_dtor 表示该对象是否有C++/OC的析构器(类似于dealloc),占1
    • 如果析构函数,则需要做析构逻辑
    • 如果没有,则可以更快的释放对象
  • shiftcls表示存储类的指针的值(类的地址), 即类信息
    • arm64中占 33位,开启指针优化的情况下,在arm64架构中有33位用来存储类指针
    • x86_64中占 44
  • magic 用于调试器判断当前对象是真的对象 还是 没有初始化的空间,占6
  • weakly_refrenced是 指对象是否被指向 或者 曾经指向一个ARC的弱变量
    • 没有弱引用的对象可以更快释放
  • deallocating 标志对象是是否正在释放内存
  • has_sidetable_rc表示 当对象引用计数大于10时,则需要借用该变量存储进位
  • extra_rc(额外的引用计数) ,表示该对象的引用计数值,实际上是引用计数值减1
    • 如果对象的引用计数为10,那么extra_rc为9(这个仅为举例说明),实际上iPhone 真机上的 extra_rc 是使用 19位来存储引用计数的

clsisa 关联原理就是isa指针中的shiftcls位域中存储了类信息,其中initInstanceIsa的过程是将 calloc 指针 和当前的 类cls 关联起来。

objc_class & objc_object

所有Class都是以objc_class为模版继承而来的。isa指针的类型是Class,由objc_class定义的类型。

而结构体类型objc_class继承自objc_object。obje_object是一个结构体,且有一个isa属性,因此objc_class也拥有isa属性

NSObject中的isa在底层是由Class 定义的,其中class的底层编码来自 objc_class类型,所以NSObject也拥有了isa属性

总结

  • 所有的对象 + + 元类 都有isa属性
  • 所有的对象都是由objc_object继承来的,类继承自objc_class
  • 简单概括就是万物皆对象,万物皆来源于objc_object,有以下两点结论:
    • 所有以 objc_object为模板 创建的对象,都有isa属性
    • 所有以objc_class为模板,创建的,都有isa属性
  • 在结构层面可以通俗的理解为上层OC底层对接
    • 下层是通过 结构体 定义的 模板,例如objc_class、objc_object
    • 上层 是通过底层的模板创建的 一些类型,例如CJLPerson
      请添加图片描述

objc_class的定义:

struct objc_class : objc_object {// Class ISA; //8字节Class superclass; //Class 类型 8字节cache_t cache;             // formerly cache pointer and vtableclass_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags
}
  • isa属性:继承自objc_objectisa,占 8字节

  • superclass 属性:Class类型,Class是由objc_object定义的,是一个指针,占8字节

  • cache属性:简单从类型class_data_bits_t目前无法得知,而class_data_bits_t是一个结构体类型,结构体内存大小需要根据内部的属性来确定,而结构体指针才是8字节

  • bits属性:只有首地址经过上面3个属性的内存大小总和的平移,才能获取到bits

类信息的静态与动态存储(ro、rw、rwe机制)

初始时,只有ro

当需要修改类信息的时候,rw引用ro,并拷贝一部分信息。

将需要动态更新的部分提取出来,存⼊rwe:

image-20220506113406159

总结:程序加载时方法存在ro。当类第一次使用的时候,rw就会引用ro;如果动态修改,就会从ro拷贝到rwe;修改的时候也是去操作rwe

rw和ro的区别ro放在纯净的内存空间(clean memory),是只读的。rw在运行生成,存放在可以读写的内存空间中(dirty memory),一经使用,ro就会成为rw的一部分(通过指针引用)。并且rw对于真正需要修改的内容,还会拆分出class_rw_ext_t;简称rwe

cache

其中,cache类的内存大小为12+2+2=16字节。分别有:buckets(8字节),mask(4字节)或者_maskAndBuckets(8字节), _mask_unused(4字节) 加上 _flag(2字节), _occupied(2字节)。

注意:buckets是方法缓存中的一个哈希表数组,储存了SEL和IMP。而mask使用来计算哈希索引的。这两个可以快速定位方法缓存。

img

sel-imp是在cache_t_buckets属性中,而在cache_t结构体中提供了获取_buckets属性的方法buckets()

我们可以通过相应的获取方法sel() 以及 imp(pClass)获得对应的sel-imp

bits

类的首地址平移32字节即可得到bits,bits储存信息的类型是class_rw_t,其中可以获取属性列表和方法列表

请添加图片描述

通过由class_rw_t提供的propertoes方法,可以获取到一个实际类型property_array_t的变量,其中有一个list的类型是property_list_t,是一个指针,存储了 property_list,即属性列表。所以bits中储存了属性列表。

通过由class_rw_t提供methods方法, 可以获取到一个实际类型method_array_t的变量,其中的list指针,类型是method_list_t,里面就是具体的方法列表。注意,该列表里面只有实例方法。类方法存储在元类的bits属性中,获取方法与该方法相同,毕竟类是元类的实例。

在class_rw_t中,还有一个ro方法,其返回类型是class_ro_t,其中有一个类型为ivar_list_tivars属性,其中储存着成员变量列表。该成员变量列表不仅储存着{}定义的成员变量,还存储着属性定义的成员变量。

class_rw_t中的相关方法

继承链

首先是最经典的图片:
请添加图片描述

  • isa走位

    isa的走向有以下几点说明:

    • 实例对象(Instance of Subclass)isa 指向 类(class)
    • 类对象(class) isa 指向 元类(Meta class)
    • 元类(Meta class)isa 指向 根元类(Root metal class)
    • 根元类(Root metal class)isa 指向它自己本身,形成闭环,这里的根元类就是NSObject

    superclass走位

    superclass(即继承关系)的走向也有以下几点说明:

    • 类之间的继承关系

      • 类(subClass) 继承自 父类(superClass)
      • 父类(superClass) 继承自 根类(RootClass),此时的根类是指NSObject
      • 根类 继承自 nil,所以根类NSObject可以理解为万物起源,即无中生有
    • 元类也存在继承,元类之间的继承关系如下:

      • 子类的元类(metal SubClass) 继承自 父类的元类(metal SuperClass)

      • 父类的元类(metal SuperClass) 继承自 根元类(Root metal Class

      • 根元类(Root metal Class) 继承于 根类(Root class),此时的根类是指NSObject

    • 【注意】实例对象之间没有继承关系之间有继承关系

isKindOfClass和isMemberOfClass

isKindOfClass:

  • 一切皆从调用者obj的isa开始,然后顺着superclass走下去,直到找到cls或superclass为nil结束
  • 当superclass为nil,意味着最后的根类NSObject也不是cls,返回flase。

类调用

判断顺序: SubClass -> MetaClass->MetaClass->...->RootMetaClass->NSObject

其中, 类对象调用方法的本质 是判断 cls 是不是 元类的继承链 上的任意一个

元类调用

MetaClassISA 指向 RootMetaClass ,所以从 RootMetaClass 开始比较判断是不是我们传入的cls,如果不是,再看根类NSObject是不是,如果NSObject也不是,就彻底没有了,返回false,

img

根元类的父类是NSObject

cls判断顺序如图:MetaClass -> RootMetaClass->NSObject

**对象调用:**cls判断顺序如图所示:object -> SubClass -> SubClass ->...->NSObject

本质是判断 cls 是不是 类继承链 上的任意一个

isMemberofClass

不用像isKindOfClass循环直到找到或nil,他只要比较cls是不是我当前的isa指向,是返回true,不是返回false。

这个方法单纯地用来判断,cls是不是调用者的isa !!

类对象调用

传入任何类对象都是false,因为类的isa指向元类

元类对象调用

元类的isa指向根元类NSObjcet,传入任何类对象也都是false

对象调用:

只要判断对象的isa,也就是图中的SubClass是不是我们传入的cls

  1. 只判断自己的类对象是不是传入的cls
  2. 只接受类对象传入 ,因为没有isa,不存在元类的介入

总结
isMemberOfClass

  • 对象调用:判断对象的isa(类对象)是否等于传入的cls,只判断本类,不查父类。

    • 查询链:对象 → isa(类对象)== cls ?
  • 类对象调用:判断类对象的isa(元类)是否等于cls。

    • 查询链:类对象 → isa(元类)== cls ?
  • 元类对象调用:判断元类对象的isa(根元类)是否等于cls。

    • 查询链:元类对象 → isa(根元类)== cls ?

isKindOfClass

  • 对象调用:判断对象的isa(类对象)是否等于cls,不等则沿superclass链查找父类,直到NSObject或nil。

    • 查询链:对象 → isa(类对象)→ superclass → … → NSObject → nil
  • 类对象调用:判断类对象的isa(元类)及其superclass链是否等于cls,最终会查到NSObject。

    • 查询链:类对象 → isa(元类)→ superclass(父类元类)→ … → 根元类 → superclass(NSObject类对象)→ nil
  • 元类对象调用:判断元类对象的isa(根元类)及其superclass链是否等于cls,最终会查到NSObject类对象。

    • 查询链:元类对象 → isa(根元类)→ superclass(NSObject类对象)→ nil

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/90639.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/90639.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

代码随想录day46dp13

647. 回文子串 题目链接 文章讲解 回溯法 class Solution { public:int count 0;// 检查字符串是否是回文bool isPalindrome(string& s, int start, int end) {while (start < end) {if (s[start] ! s[end]) return false;start;end--;}return true;}// 回溯法&#…

学习随笔录

#61 学习随笔录 今日的思考 &#xff1a; 反思一下学习效率低下 不自律 或者 惰性思维 懒得思考 又或者 好高婺远 顶级自律从不靠任何意志力&#xff0c;而在于「平静如水的野心」_哔哩哔哩_bilibili 然后上面是心灵鸡汤合集 vlog #79&#xff5c;程序员远程办公的一天…

python-函数进阶、容器通用方法、字符串比大小(笔记)

python数据容器的通用方法#记住排序后容器类型会变成list容器列表 list[1,3,5,4,6,7] newListsorted(list,reverseTrue) print(newList) [7, 6, 5, 4, 3, 1]list[1,3,5,4,6,7] newListsorted(list,reverseFalse) print(newList) [1, 3, 4, 5, 6, 7]字典排序的是字典的key字符串…

关闭chrome自带的跨域限制,简化本地开发

在开发时为了图方便,简化本地开发,懒得去后端配置允许跨域,那就可以用此方法1. 右键桌面上的Chrome浏览器图标&#xff0c;选择“创建快捷方式”到桌面。2. 在新创建的快捷方式的图标上右键&#xff0c;选择“属性”。3. 在弹出窗口中的“目标”栏中追加&#xff1a; --allow-r…

C++___快速入门(上)

第一个C程序#include<iostream> using namespace std; int main() {cout << "hello world !" << endl;return 0; }上边的代码就是用来打印字符串 “hello world !” 的&#xff0c;可见&#xff0c;与C语言还是有很大的差别的&#xff0c;接下来我…

构建企业级Docker日志驱动:将容器日志无缝发送到腾讯云CLS

源码地址:https://github.com/k8scat/docker-log-driver-tencent-cls 在现代云原生架构中,容器化应用已经成为主流部署方式。随着容器数量的快速增长,如何高效地收集、存储和分析容器日志成为了一个关键挑战。传统的日志收集方式往往存在以下问题: 日志分散在各个容器中,难…

Kafka——消费者组重平衡能避免吗?

引言 其实在消费者组到底是什么&#xff1f;中&#xff0c;我们讲过重平衡&#xff0c;也就是Rebalance&#xff0c;现在先来回顾一下这个概念的原理和用途。它是Kafka实现消费者组&#xff08;Consumer Group&#xff09;弹性伸缩和容错能力的核心机制&#xff0c;却也常常成…

使用爬虫获取游戏的iframe地址

如何通过爬虫获取游戏的iframe地址要获取网页中嵌入的游戏的iframe地址&#xff08;即iframe元素的src属性&#xff09;&#xff0c;您可以使用网络爬虫技术。iframe是HTML元素&#xff0c;用于在当前页面中嵌入另一个文档&#xff08;如游戏页面&#xff09;&#xff0c;其地址…

NTLite Ent Version

NTLite是一款专业的系统安装镜像制作工具&#xff0c;通过这款软件可以帮助用户快速生成镜像文件打好补丁&#xff0c;很多朋友在安装电脑系统的时候一般都安装了windows系统的所有Windows组件&#xff0c;其实有很多Windows组件你可能都用到不到&#xff0c;不如在安装系统时就…

Maven之依赖管理

Maven之依赖管理一、Maven依赖管理的核心价值二、依赖的基本配置&#xff08;坐标与范围&#xff09;2.1 依赖坐标&#xff08;GAV&#xff09;2.2 依赖范围&#xff08;scope&#xff09;示例&#xff1a;常用依赖范围配置三、依赖传递与冲突解决3.1 依赖传递性示例&#xff1…

【Unity实战100例】Unity资源下载系统开发流程详解(移动端、PC端 ,局域网控制台服务)

目录 一、项目概述 二、服务器开发 1、配置文件设计 1、加载配置 2. 处理客户端请求 3. 文件下载处理 三、客户端开发 1、配置管理 1、配置加载与保存 2、下载任务管理 1、任务类设计 2、下载队列管理 3、核心下载流程 四、UI系统实现 五、部署与测试 1、服务…

[Python] -进阶理解7- Python中的内存管理机制简析

Python(尤其是 CPython)采用自动内存管理机制,核心包括引用计数(Reference Counting)与垃圾回收机制(Garbage Collection),并配合专门的内存池和分配器机制来提升效率与减少碎片。 这套机制隐藏在开发者视线之外,Python 开发者无需手动申请或释放内存。 二、Python 内…

云祺容灾备份系统AWS S3对象存储备份与恢复实操手册

1、创建密钥访问AWS控制台&#xff0c;鼠标移至右上角账户处&#xff0c;在弹出菜单中点击安全凭证&#xff0c;如图1。图1在弹出页面中&#xff0c;下滑找到访问密钥&#xff0c;并点击创建访问密钥&#xff0c;如图2。图2选择其他&#xff0c;并点击下一步&#xff0c;如图3。…

使用 LLaMA 3 8B 微调一个 Reward Model:从入门到实践

本文将介绍如何基于 Meta 的 LLaMA 3 8B 模型构建并微调一个 Reward Model&#xff0c;它是构建 RLHF&#xff08;基于人类反馈的强化学习&#xff09;系统中的关键一环。我们将使用 Hugging Face 的 transformers、trl 和 peft 等库&#xff0c;通过参数高效微调&#xff08;L…

matrix-breakout-2-morpheus靶场攻略

靶场使用将压缩包解压到一个文件夹中&#xff0c;用虚拟机应用新建虚拟机&#xff0c;扫描虚拟机&#xff0c;扫描那个文件夹&#xff0c;就可以把虚拟机扫出来了&#xff0c;然后启动虚拟机这时候靶场启动后&#xff0c;咱们现在要找到这个靶场。靶场是网页形式的&#xff0c;…

MySQL 复制表

MySQL 复制表 概述 在数据库管理中&#xff0c;复制表是一项常用的操作。它允许数据库管理员将一个表中的数据复制到另一个表中&#xff0c;无论是同一个数据库还是不同的数据库。MySQL数据库提供了多种方法来复制表&#xff0c;本文将详细介绍MySQL复制表的过程、方法及其应用…

『哈哥赠书 - 55期』-『码农职场:IT人求职就业手册』

文章目录⭐️ 码农职场&#xff1a;IT人求职就业手册⭐️ 本书简介⭐️ 作者简介⭐️ 编辑推荐这是一本专为广大IT行业求职者量身定制的指南&#xff0c;提供了从职前准备到成功就业的全方位指导&#xff0c;涵盖了职业目标规划、自我技能评估、求职策略、简历准备以及职场心理…

单片机学习课程

单片机学习课程 课程介绍 单片机技术作为现代工业自动化、电子电气、通信及物联网等领域的主流技术&#xff0c;早已深度融入我们生活与生产的各个角落。从常见家电到自动化公共设施&#xff0c;都离不开单片机的支持。同时&#xff0c;它也是学习 ARM 嵌入式系统、FPGA 设计等…

【AcWing 143题解】最大异或对

AcWing 143. 最大异或对 【题目描述】 在查看解析之前&#xff0c;先给自己一点时间思考哦&#xff01; 【题解】 本题要求给定一个整数序列&#xff0c;找出其中任意两个数进行异或运算后&#xff0c;结果的最大值是多少。由于数据规模较大&#xff0c;我们不能简单地通过两…

SQLAlchemy 2.0简单使用

记录一下SQLAlchemy 2.0连接mysql数据库的方法及简单使用 环境及依赖 Python:3.8 mysql:8.3 Flask:3.0.3 SQLAlchemy:2.0.37 PyMySQL:1.1.1使用步骤 1、创建引擎&#xff0c;链接到mysql engine create_engine(mysqlpymysql://{username}:{password}{ip}:3306/{database_name}…