【iOS】类结构分析

前言

之前我们已经探索得出对象的本质就是一个带有isa指针的结构体,这篇文章来分析一下类的结构以及类的底层原理。

类的本质

类的本质

我们在main函数中写入以上代码,然后利用clang对其进行反编译,可以得到c++文件

可以看到底层使用Class接收,接下来找到Class的定义

发现Class在底层定义为一个名为objc_class的结构体

查找objc_class,发现它继承自objc_object

可以得出:类的本质是objc_class类型的结构体,objc_class继承自objc_object,因此可以一句话概括——万物皆对象

objc_class&objc_object、objc&NSObject的关系

objc_object和NSObject

为什么说继承自objc_object就满足万物皆对象?

我们看到NSObject的定义

对比objc_object的定义:

仔细比较可以看出:

  • 其实NSObject是objc_object的仿写,和objc_object的定义是一样的,在底层会编译成objc_object

  • 同理NSObject类是OC版本的objc_class

而之前我们学习过的NSObject_IMPL则是NSObject编译到objc_object的中间产物,并且实例伪继承自NSObject_IMPL,正如继承objc_object的皆为对象。

objc_class&objc_object

从上文我们已经得知:objc_class继承自objc_object,我们寻找objc_object的定义

可以找到两个定义:第一个位于objc.h,没有被废除,从编译的main-arm64.cpp中可以看到,使用的这个版本的objc_object。第二个位于objc-privat.h

总结objc_class与objc_object的关系如下:

  • 结构体类型objc_class继承自objc_object类型,其中objc_object也是一个结构体,且有一个isa属性,所以objc_class也拥有了isa属性

  • mian-arm64.cpp底层编译文件中,NSObject中的isa在底层是由Class 定义的,其中class的底层编码来自 objc_class类型,所以NSObject也拥有了isa属性

  • NSObject是一个类,用它初始化一个实例对象objc,objc 满足objc_object的特性(即有isa属性),主要是因为isa 是由 NSObject 从objc_class继承过来的,而objc_class继承自objc_object,objc_object 有isa属性。所以对象都有一个isa,isa表示指向,来自于当前的objc_object

  • objc_object(结构体)是当前的根对象,所有的对象都有这样一个特性objc_object,即拥有isa属性

objc_object 与 对象的关系

  • 所有的对象都是以 objc_object 为模板继承过来的

  • 所有的对象是来自 NSObject(OC),但是真正到底层的是一个objc_object(C/C++)的结构体类型

  • objc_object对象的关系 是 继承关系

总结

所有的对象 + 类 + 元类 都有isa属性

所有的对象都是由objc_object继承来的

简单概括就是万物皆对象,万物皆来源于objc_object,有以下两点结论:

  • 所有以 objc_object为模板创建的对象,都有isa属性

  • 所有以 objc_class为模板创建的类,都有isa属性

在结构层面可以通俗的理解为上层OC与底层的对接:

  • 下层是通过结构体定义的模板,例如objc_class、objc_object

  • 上层是通过底层的模板创建的一些类型,例如TCJPerson

objc_class、objc_object、isa、object、NSObject等的整体的关系,如下图:

类的结构

从objc_class的定义可以得出,类有4个属性:

  • Class ISA:类对象与实例对象一样,同样有isa指针,类对象的isa指针关联着元类,Class本身就是一个指针,占用8字节(这个属性是继承自objc_object的)

  • Class superclass:即类的父类,Class类型,占用8个字节

  • cache_t cache

cache_t是一个结构体,内存长度由所有元素决定:_bucketsAndMaybeMask是long类型,它是一个指针,占用8字节; mask_t是个uint32_t类型,_mask占用4字节;因_occupied和_flags都是uint16_t类型,uint16_t是 unsigned short 的别名,所以_occupied占用2字节;flags占用2字节=>cache_t占用16字节。这个属性其实是用来保存方法缓存的,后续博客中会详细介绍。

  • class_data_bits_t bits

class_data_bits_t bits

class_data_bits_t bits这个属性用来存数据,类的属性和方法就保存在这里。可以看到objc_class中有一个class_rw_t *data()方法。

class_rw_t是在运行时生成的,它在realizeClass中生成,它包含了class_ro_t.它在_objc_init方法中关于dyld的回调的map_images中最终将分类的方法与协议都插入到自己的方法列表、协议列表中.它不包含成员变量列表,因为成员变量列表是在编译期就确定好的,它只保存在class_ro_t中.不过,class_rw_t中包含了一个指向class_ro_t的指针

类的属性方法

类的属性

通过LLDB调试我们可以发现,bits当中存储的信息类型是class_rw_t,是一个结构体类型,在这个结构体中有提供相应的方法去获取属性列表、方法列表等。

通过LLDB进一步调试可以发现,这个属性列表中只有属性,没有成员变量。属性与成员变量的区别就是有没有set、get方法,如果有,则是属性,如果没有,则是成员变量。

除此之外,class_rw_t中有个属性class_ro_t,class_ro_t是在编译期生成的,它存储了当前类在编译期就已经确定的属性、方法以及协议,它里面没有分类中定义的方法和协议。而成员变量就存放在ro的ivars里面

总结如下:

  • 通过{}定义的成员变量,会存储在类的bits属性中,通过bits --> data() -->ro() --> ivars获取成员变量列表,除了包括成员变量,还包括属性定义的成员变量

  • 通过@property定义的属性,也会存储在bits属性中,通过bits --> data() --> properties() --> list获取属性列表,其中只包含属性

类的方法

类的实例方法存储在类的bits属性中,通过bits --> methods() --> list获取实例方法列表,参观游客实例方法,方法列表中还包含属性的set方法和get方法,以及系统在底层添加的C++的.cxx_destruct方法。

类的类方法存储在元类的bits属性中,通过元类bits --> methods() --> list获取类方法列表。

结论

  • 成员变量存放在ivar

  • 属性存放在property,同时也会存一份在ivar,并生成setter、getter方法

  • 对象方法存放在类里面

  • 类方法存放在元类里面

补充

类存在几份

由于类的信息在内存中永远只存在一份,所以 类对象只有一份。

isKindOfClass和isMerberOfClass的理解

对于isMerberOfClass,方法会对比元类或类本身与某个类(元类还是类取决于类方法还是实例方法,总之就是取isa指针指向的东西)

而对于isKindOfClass,走以下逻辑:

最后给出一张isa的走位图,实现为superclass指向,虚线为isa指向

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

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

相关文章

Vanna.AI:解锁连表查询的新境界

Vanna.AI:解锁连表查询的新境界 在当今数字化时代,数据已成为企业决策的核心驱动力。然而,从海量数据中提取有价值的信息并非易事,尤其是当数据分散在多个表中时,连表查询成为了数据分析师和开发者的日常挑战。传统的…

前端流行框架Vue3教程:24.动态组件

24.动态组件 有些场景会需要在两个组件间来回切换&#xff0c;比如 Tab 界面 我们准备好A B两个组件ComponentA ComponentA App.vue代码如下&#xff1a; <script> import ComponentA from "./components/ComponentA.vue" import ComponentB from "./…

海拔案例分享-实践活动报名测评小程序

大家好&#xff0c;今天湖南海拔科技想和大家分享一款实践活动报名测评小程序&#xff0c;客户是长沙一家专注青少年科创教育的机构&#xff0c;这家机构平时要组织各种科创比赛、培训课程&#xff0c;随着学员增多&#xff0c;管理上的问题日益凸显&#xff1a;每次组织活动&a…

【MySQL】CRUD

CRUD 简介 CRUD是对数据库中的记录进行基本的增删改查操作 Create&#xff08;创建&#xff09;Retrieve&#xff08;读取&#xff09;Update&#xff08;更新&#xff09;Delete&#xff08;删除&#xff09; 一、新增&#xff08;Create&#xff09; 语法&#xff1a; I…

【数据架构04】数据湖架构篇

✅ 10张高质量数据治理架构图 无论你是数据架构师、治理专家&#xff0c;还是数字化转型负责人&#xff0c;这份资料库都能为你提供体系化参考&#xff0c;高效解决“架构设计难、流程不清、平台搭建慢”的痛点&#xff01; &#x1f31f;限时推荐&#xff0c;速速收藏&#…

【Java Web】3.SpringBootWeb请求响应

&#x1f4d8;博客主页&#xff1a;程序员葵安 &#x1faf6;感谢大家点赞&#x1f44d;&#x1f3fb;收藏⭐评论✍&#x1f3fb; 文章目录 一、请求 1.1 postman 1.2 简单参数 1.3 实体参数 1.4 数组集合参数 1.5 日期参数 1.6 JSON参数 1.7 路径参数 二、响应 2…

竞争性学习:无监督世界的智能聚类引擎

一、竞争性学习&#xff1a;无监督聚类的生物启发范式 1.1 核心原理&#xff1a;神经元的 “适者生存” 竞争性学习模拟生物神经网络的竞争机制&#xff1a;多个神经元对输入数据 “竞争响应”&#xff0c;获胜神经元&#xff08;与输入最匹配&#xff09;更新权重&#xff0…

docker面试题(5)

Docker安全么 Docker 利用了 Linux 内核中很多安全特性来保证不同容器之间的隔离&#xff0c;并且通过签名机制来对镜像进行 验证。大量生产环境的部署证明&#xff0c;Docker 虽然隔离性无法与虚拟机相比&#xff0c;但仍然具有极高的安全性。 如何清理后台停止的容器 可以使用…

同为科技 智能PDU产品选型介绍 EN10/I801CI

智能PDU是一种利用信息技术手段&#xff0c;优化电力的分配和使用。随着数据中心进行虚拟化部署和为提高计算效率而整合设备&#xff0c;平均机架功率密度在持续增长&#xff0c;几年前&#xff0c;一个普通机柜需要3-4千瓦电力&#xff0c;而现今9-15千瓦甚至更高电力的机柜则…

Aciviti工作流

1. springBoot和activiti整合 pom.xml文件 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"…

golang 对象池sync.Pool的实现

Go语言中sync.Pool通过多级缓存机制实现高效对象复用&#xff0c;其核心设计结合了GMP调度模型特性。以下是实现要点分析&#xff1a; P o o l ∑ p 0 G O M A X P R O C S ( l o c a l P o o l p ) v i c t i m C a c h e Pool \sum_{p0}^{GOMAXPROCS}(localPool_p) vict…

Docker run命令-p参数详解

端口映射基础语法 docker run -p <宿主机端口>:<容器端口> 操作示例 docker run -d --restartalways --namespug -p 5000:80 registry.aliyuncs.com/openspug/spug参数解析 -d&#xff1a;后台运行容器--restartalways&#xff1a;设置容器自动重启--namespug&…

《2.1.4 C语言中的整数类型及类型转换|精讲篇》

后面作者会在2025.5.25 00:00前整理出笔记和思维导图大家放心&#xff0c;主页还有其他文章 请先移步欢迎参考 收藏文章 关注博主 高效学习 好了&#xff0c;这小节我们要探讨一个相对来说简单的问题&#xff0c;就是C语言里边的那些定点整数是如何进行强制类型转换的。好来看这…

采用多维计算策略(分子动力学模拟+机器学习),显著提升 α-半乳糖苷酶热稳定性

字数 978&#xff0c;阅读大约需 5 分钟 在工业应用领域&#xff0c;α-半乳糖苷酶在食品加工、动物营养及医疗等方面发挥着重要作用。然而&#xff0c;微生物来源的该酶往往存在热稳定性不足的问题&#xff0c;限制了其在工业场景中的高效应用。近日&#xff0c;来自江南大学的…

Jetpack Compose预览调试技巧

Jetpack Compose 预览(Preview)不显示是一个常见问题,可能由多种原因导致。以下是系统的调试技巧和解决方案: 1. 检查基础配置 Compose 版本兼容性 确保 compose-compiler、compose-ui 等依赖版本一致且与 Kotlin 版本兼容。检查 build.gradle: android {compileOptions {…

使用 Go 语言实现完整且轻量级高性能的 MQTT Broker

MQTT&#xff08;Message Queuing Telemetry Transport&#xff09;是一种轻量级的发布/订阅消息传输协议。但是目前虽然mqtt的客户端很多&#xff0c;但是服务端着实不多&#xff0c;常见的服务端如mosquitto或emqx。但是golang语言的实现几乎找不到。golang的轻量级部署和高并…

uv sync --frozen卡住不动

今天受邀帮同事调试uv卡住不动的问题&#xff0c;同样的代码已经在别的服务器跑起来了&#xff0c;换了一台服务器之后&#xff0c;执行uv sync --frozen没有按预期创建虚拟环境和安装依赖。 1. 镜像源是已经配置好的&#xff0c;pip install也能很快安装包。 2. 查看了uv.lo…

Spring Boot中如何对密码等敏感信息进行脱敏处理

以下是常见的脱敏方法及实现步骤&#xff0c;涵盖配置、日志和API响应等多个层面&#xff1a; ​1. 配置文件敏感信息脱敏​ (1) 使用加密库&#xff08;如Jasypt&#xff09; ​步骤​&#xff1a; 添加依赖&#xff1a; <dependency><groupId>com.github.ulise…

springboot中redis的事务的研究

redis的事务类似于队列操作&#xff0c;执行过程分为三步&#xff1a; 开启事务入队操作执行事务 使用到的几个命令如下&#xff1a; 命令说明multi开启一个事务exec事务提交discard事务回滚watch监听key(s)&#xff1a;当监听一个key(s)时&#xff0c;如果在本次事务提交之…

python打卡day35@浙大疏锦行

知识点回顾&#xff1a; 三种不同的模型可视化方法&#xff1a;推荐torchinfo打印summary权重分布可视化进度条功能&#xff1a;手动和自动写法&#xff0c;让打印结果更加美观推理的写法&#xff1a;评估模式 作业&#xff1a;调整模型定义时的超参数&#xff0c;对比下效果。…