iOS中,类,分类,拓展分别储存在哪里?
在 iOS 中,类、分类(Category)、拓展(Extension)的存储位置与其数据类型和编译阶段有关,具体如下:
一、类(Class)的数据存储
类的信息分为 编译期定义的数据 和 运行时动态生成的数据,存储在不同区域:
1. 编译期数据(静态存储)
类名、继承关系、属性声明、方法声明 等信息存储在 Mach-O 文件 的 __DATA 段或 __TEXT 段(如 ro 数据)。
具体通过 class_ro_t 结构体(objc_class 中的 bits 字段指向)存储,属于 只读数据,存储在 内存的只读区域(如代码段)。
2. 运行时数据(动态存储)
方法实现、协议列表、关联对象 等运行时动态添加的数据,通过 class_rw_t 结构体(从 bits.data() 获取)管理,存储在 堆内存 或 数据段(可读写区域)。
类对象(objc_class 实例)本身存储在 堆内存,由 Runtime 管理生命周期。
二、分类(Category)的数据存储
分类是 运行时动态加载 的逻辑扩展,其数据存储特点:
1. 分类元数据
分类的方法、属性(通过 @property 结合关联对象实现)、协议等信息,编译后生成 category_t 结构体,存储在 Mach-O 文件 的 __DATA 段(如 __objc_catlist 节)。
运行时,Runtime 会将分类的 category_t 合并到类的 class_rw_t 中(方法列表、协议列表等),存储在类对象的 堆内存数据区。
2. 分类方法的存储
分类的实例方法和类方法实现,存储在 代码段(与类的方法实现一起),属于 只读数据。
若分类与类存在方法名冲突,分类方法会覆盖类的原方法(运行时方法查找顺序决定)。
三、拓展(Extension)的数据存储
拓展(匿名分类)是 编译期特性,本质是类的私有声明,存储特点:
1. 声明阶段
拓展中声明的属性(实例变量)、方法,在编译时直接合并到 类的 class_ro_t 结构体 中,与类的原始声明无异。
因此,拓展的 属性声明会生成实例变量,存储在类对象的 ro 数据中(只读区域),而 方法声明 存储在 class_ro_t 的方法列表中。
2. 实现阶段
拓展中声明的方法必须在 类的 .m 文件中实现,实现代码存储在 代码段(只读区域),与类的其他方法一起。
拓展无法像分类一样在运行时动态添加,属于类的 静态组成部分。
总结:存储位置对比
类型 编译期存储位置 运行时存储位置 数据性质
关键说明
• 分类与拓展的本质区别:
分类是 运行时扩展,拓展是 编译期绑定,前者通过 category_t 动态合并,后者直接编译到类结构中。
• 内存区域安全性:
只读数据(如 ro、代码段)存储在不可写区域,防止运行时篡改;可写数据(如 rw、关联对象)存储在堆或数据段,支持动态修改。