前言: iOS 高级之美 是本人总结了一些工作实际开发研究以及面试重点,围绕底层进行 源码分析
- LLDB 调试
- 源码断点
- 汇编调试
,让读者真正感受 Runtime底层之美~😊
目录如下:
iOS 高级之美(一)—— iOS_objc4-756.2 最新源码编译调试
iOS 高级之美(二)—— OC对象底层上篇
iOS 高级之美(三)—— OC对象底层下篇
iOS 高级之美(四)—— isa原理分析
iOS 高级之美(五)—— 类结构分析上篇
iOS 高级之美(六)—— malloc分析
iOS 高级之美(七)—— 24K纯技术男~KC_2019年终总结
iOS 高级之美(八)—— 类结构分析中篇
iOS 高级之美(九)—— 类结构分析下篇

一、文章前言

我们通过 LLDB
分析得出:
cls
的类结构信息
0x0000000100000f9d
是我们相应 isa
的指向
0x0000000800000003
就是我们的父类
0x0000000100000010
就是我们常说的cache_t
信息
0x0000000100000f54
就是我们 data()
数据的落地之处!这个篇章我们就是针对这个家伙展开分析
二、class_data_bits_t 分析
struct class_data_bits_t {}
结构体类型,我们通过-
uintptr_t bits
是一个 uintptr_t
类型的指针,在编译阶段会指向 class_ro_t
,在运行时会封装成 class_rw_t
2.1、class_ro_t 分析
class_ro_t
存储了很多在编译时期就确定的类的信息。其内部包含了类名、ivar、方法、属性等。是只读类型的结构。
struct class_ro_t {
uint32_t flags; //配合mask 可以用来判断,元类,根类等
uint32_t instanceStart; //non-fragile判断依据
uint32_t instanceSize;
#ifdef __LP64__
uint32_t reserved;
#endif
const uint8_t * ivarLayout;
const char * name;
method_list_t * baseMethodList;
protocol_list_t * baseProtocols;
const ivar_list_t * ivars;
const uint8_t * weakIvarLayout;
property_list_t *baseProperties;
method_list_t *baseMethods() const {
return baseMethodList;
}
};
其中 instanceStart
和 instanceSize
是为了实现 Non Fragile
特性。
In the legacy runtime, if you change the layout of instance variables in a class, you must recompile classes that inherit from it.
In the modern runtime, if you change the layout of instance variables in a class, you do not have to recompile classes that inherit from it.
下面我们来读取下 class_ro_t
的信息,为了证明 class_ro_t
是在编译期期确定,我们要在 runtime
的入口下个符号断点 objc_init
(这也可以先运行一次,先得到类对象的地址,第二次符号断点直接使用)。由于 objc_init
是 runtime
入口(runtime还没有处理类的元数据)。

2.2、class_rw_t
从上面的章节看出 class_ro_t
存储的大多是类在编译时就已经确定的信息,但是 Objective-C
又是一门动态语言,因此需要另一个可以运行时 读写
数据,也就是 class_rw_t
。
struct class_rw_t {
// Be warned that Symbolication knows the layout of this structure.
uint32_t flags; //同样结合mask 使用
uint32_t version;
const class_ro_t *ro; //ro的指针
method_array_t methods;
property_array_t properties;
protocol_array_t protocols;
Class firstSubclass;
Class nextSiblingClass;
char *demangledName;
#if SUPPORT_INDEXED_ISA
uint32_t index;
#endif
...
};
class_rw_t
提供了运行时对类拓展的能力,存有类的方法、属性(成员变量)、协议等信息。class_rw_t
的内容是可以在运行时被动态修改的,可以说运行时对类的拓展大都是存储在这里的。
demangledName
是计算机语言用于解决实体名称唯一性的一种方法,做法是向名称中添加一些类型信息,用于从编译器中向链接器传递更多语义信息
值得注意的是 method_array_t
的结构会有三种变化。
- 1️⃣:空。
- 2️⃣:一个一维数组的指针。(默认为一维数组,就算一个类有
category
,如果没有实现 +load
,就也是一维数组,没有实现 +load
,就意味这个类可以懒加载)
- 3️⃣:指向列表的指针数组。(当一个类存在
category
且这个类无法懒加载,或者动态修改方法列表如 addMethod
。就会生成二维列表,)
如果从二进制的角度讲,只要这个类存在于 __objc_catlist
或者 __objc_nlcatlist
,那么他的 method_array_t
就为二维数组。
那么 class_rw_t
是什么时候被赋值的呢。
三、类的初始化
3.1、realizeClass
首先简单看一个runtime初始化的流程。
类的可读写元数据的初始话主要发生在 realizeClass
和 methodizeClass
```c
static Class realizeClass(Class cls)
{
runtimeLock.assertLocked();
...
// fixme verify class is not in an un-dlopened part of the shared cache?
//RO_FUTURE 标志位来记录是否可以直接强转。
ro = (const class_ro_t *)cls->data();
if (ro->flags & RO_FUTURE) {
// This was a future class. rw data is already allocated.
rw = cls->data();
ro = cls->data()->ro;
cls->changeInfo(RW_REALIZED|RW_REALIZING, RW_FUTURE);
} else {
//不能转换,则开辟空间。
// Normal class. Allocate writeable class data.
rw = (class_rw_t *)calloc(sizeof(class_rw_t), 1);
rw->ro = ro;
rw->flags = RW_REALIZED|RW_REALIZING;
cls->setData(rw);
}
isMeta = ro->flags & RO_META;
rw->version = isMeta ? 7 : 0; // old runtime went up to 6
...
优先执行supercls 和metacls 的realize
检测isa 优化
更新实例大小,关联子类父类,元类根类。
...
前言: iOS 高级之美 是本人总结了一些工作实际开发研究以及面试重点,围绕底层进行 源码分析
- LLDB 调试
- 源码断点
- 汇编调试
,让读者真正感受 Runtime底层之美~😊
目录如下:
iOS 高级之美(一)—— iOS_objc4-756.2 最新源码编译调试
iOS 高级之美(二)—— OC对象底层上篇
iOS 高级之美(三)—— OC对象底层下篇
iOS 高级之美(四)—— isa原理分析
iOS 高级之美(五)—— 类结构分析上篇
iOS 高级之美(六)—— malloc分析
iOS 高级之美(七)—— 24K纯技术男~KC_2019年终总结
iOS 高级之美(八)—— 类结构分析中篇
iOS 高级之美(九)—— 类结构分析下篇

一、文章前言

我们通过 LLDB
分析得出:
cls
的类结构信息
0x0000000100000f9d
是我们相应 isa
的指向
0x0000000800000003
就是我们的父类
0x0000000100000010
就是我们常说的cache_t
信息
0x0000000100000f54
就是我们 data()
数据的落地之处!这个篇章我们就是针对这个家伙展开分析
二、class_data_bits_t 分析
struct class_data_bits_t {}
结构体类型,我们通过-
uintptr_t bits
是一个 uintptr_t
类型的指针,在编译阶段会指向 class_ro_t
,在运行时会封装成 class_rw_t
2.1、class_ro_t 分析
class_ro_t
存储了很多在编译时期就确定的类的信息。其内部包含了类名、ivar、方法、属性等。是只读类型的结构。
struct class_ro_t {
uint32_t flags; //配合mask 可以用来判断,元类,根类等
uint32_t instanceStart; //non-fragile判断依据
uint32_t instanceSize;
#ifdef __LP64__
uint32_t reserved;
#endif
const uint8_t * ivarLayout;
const char * name;
method_list_t * baseMethodList;
protocol_list_t * baseProtocols;
const ivar_list_t * ivars;
const uint8_t * weakIvarLayout;
property_list_t *baseProperties;
method_list_t *baseMethods() const {
return baseMethodList;
}
};
其中 instanceStart
和 instanceSize
是为了实现 Non Fragile
特性。
In the legacy runtime, if you change the layout of instance variables in a class, you must recompile classes that inherit from it.
In the modern runtime, if you change the layout of instance variables in a class, you do not have to recompile classes that inherit from it.
下面我们来读取下 class_ro_t
的信息,为了证明 class_ro_t
是在编译期期确定,我们要在 runtime
的入口下个符号断点 objc_init
(这也可以先运行一次,先得到类对象的地址,第二次符号断点直接使用)。由于 objc_init
是 runtime
入口(runtime还没有处理类的元数据)。

2.2、class_rw_t
从上面的章节看出 class_ro_t
存储的大多是类在编译时就已经确定的信息,但是 Objective-C
又是一门动态语言,因此需要另一个可以运行时 读写
数据,也就是 class_rw_t
。
struct class_rw_t {
// Be warned that Symbolication knows the layout of this structure.
uint32_t flags; //同样结合mask 使用
uint32_t version;
const class_ro_t *ro; //ro的指针
method_array_t methods;
property_array_t properties;
protocol_array_t protocols;
Class firstSubclass;
Class nextSiblingClass;
char *demangledName;
#if SUPPORT_INDEXED_ISA
uint32_t index;
#endif
...
};
class_rw_t
提供了运行时对类拓展的能力,存有类的方法、属性(成员变量)、协议等信息。class_rw_t
的内容是可以在运行时被动态修改的,可以说运行时对类的拓展大都是存储在这里的。
demangledName
是计算机语言用于解决实体名称唯一性的一种方法,做法是向名称中添加一些类型信息,用于从编译器中向链接器传递更多语义信息
值得注意的是 method_array_t
的结构会有三种变化。
- 1️⃣:空。
- 2️⃣:一个一维数组的指针。(默认为一维数组,就算一个类有
category
,如果没有实现 +load
,就也是一维数组,没有实现 +load
,就意味这个类可以懒加载)
- 3️⃣:指向列表的指针数组。(当一个类存在
category
且这个类无法懒加载,或者动态修改方法列表如 addMethod
。就会生成二维列表,)
如果从二进制的角度讲,只要这个类存在于 __objc_catlist
或者 __objc_nlcatlist
,那么他的 method_array_t
就为二维数组。
那么 class_rw_t
是什么时候被赋值的呢。
三、类的初始化
3.1、realizeClass
首先简单看一个runtime初始化的流程。
类的可读写元数据的初始话主要发生在 realizeClass
和 methodizeClass
```c
static Class realizeClass(Class cls)
{
runtimeLock.assertLocked();
...
// fixme verify class is not in an un-dlopened part of the shared cache?
//RO_FUTURE 标志位来记录是否可以直接强转。
ro = (const class_ro_t *)cls->data();
if (ro->flags & RO_FUTURE) {
// This was a future class. rw data is already allocated.
rw = cls->data();
ro = cls->data()->ro;
cls->changeInfo(RW_REALIZED|RW_REALIZING, RW_FUTURE);
} else {
//不能转换,则开辟空间。
// Normal class. Allocate writeable class data.
rw = (class_rw_t *)calloc(sizeof(class_rw_t), 1);
rw->ro = ro;
rw->flags = RW_REALIZED|RW_REALIZING;
cls->setData(rw);
}
isMeta = ro->flags & RO_META;
rw->version = isMeta ? 7 : 0; // old runtime went up to 6
...
优先执行supercls 和metacls 的realize
检测isa 优化
更新实例大小,关联子类父类,元类根类。
...
前言: iOS 高级之美 是本人总结了一些工作实际开发研究以及面试重点,围绕底层进行 源码分析
- LLDB 调试
- 源码断点
- 汇编调试
,让读者真正感受 Runtime底层之美~😊
目录如下:
iOS 高级之美(一)—— iOS_objc4-756.2 最新源码编译调试
iOS 高级之美(二)—— OC对象底层上篇
iOS 高级之美(三)—— OC对象底层下篇
iOS 高级之美(四)—— isa原理分析
iOS 高级之美(五)—— 类结构分析上篇
iOS 高级之美(六)—— malloc分析
iOS 高级之美(七)—— 24K纯技术男~KC_2019年终总结
iOS 高级之美(八)—— 类结构分析中篇
iOS 高级之美(九)—— 类结构分析下篇

一、文章前言

我们通过 LLDB
分析得出:
cls
的类结构信息
0x0000000100000f9d
是我们相应 isa
的指向
0x0000000800000003
就是我们的父类
0x0000000100000010
就是我们常说的cache_t
信息
0x0000000100000f54
就是我们 data()
数据的落地之处!这个篇章我们就是针对这个家伙展开分析
二、class_data_bits_t 分析
struct class_data_bits_t {}
结构体类型,我们通过-
uintptr_t bits
是一个 uintptr_t
类型的指针,在编译阶段会指向 class_ro_t
,在运行时会封装成 class_rw_t
2.1、class_ro_t 分析
class_ro_t
存储了很多在编译时期就确定的类的信息。其内部包含了类名、ivar、方法、属性等。是只读类型的结构。
struct class_ro_t {
uint32_t flags; //配合mask 可以用来判断,元类,根类等
uint32_t instanceStart; //non-fragile判断依据
uint32_t instanceSize;
#ifdef __LP64__
uint32_t reserved;
#endif
const uint8_t * ivarLayout;
const char * name;
method_list_t * baseMethodList;
protocol_list_t * baseProtocols;
const ivar_list_t * ivars;
const uint8_t * weakIvarLayout;
property_list_t *baseProperties;
method_list_t *baseMethods() const {
return baseMethodList;
}
};
其中 instanceStart
和 instanceSize
是为了实现 Non Fragile
特性。
In the legacy runtime, if you change the layout of instance variables in a class, you must recompile classes that inherit from it.
In the modern runtime, if you change the layout of instance variables in a class, you do not have to recompile classes that inherit from it.
下面我们来读取下 class_ro_t
的信息,为了证明 class_ro_t
是在编译期期确定,我们要在 runtime
的入口下个符号断点 objc_init
(这也可以先运行一次,先得到类对象的地址,第二次符号断点直接使用)。由于 objc_init
是 runtime
入口(runtime还没有处理类的元数据)。

2.2、class_rw_t
从上面的章节看出 class_ro_t
存储的大多是类在编译时就已经确定的信息,但是 Objective-C
又是一门动态语言,因此需要另一个可以运行时 读写
数据,也就是 class_rw_t
。
struct class_rw_t {
// Be warned that Symbolication knows the layout of this structure.
uint32_t flags; //同样结合mask 使用
uint32_t version;
const class_ro_t *ro; //ro的指针
method_array_t methods;
property_array_t properties;
protocol_array_t protocols;
Class firstSubclass;
Class nextSiblingClass;
char *demangledName;
#if SUPPORT_INDEXED_ISA
uint32_t index;
#endif
...
};
class_rw_t
提供了运行时对类拓展的能力,存有类的方法、属性(成员变量)、协议等信息。class_rw_t
的内容是可以在运行时被动态修改的,可以说运行时对类的拓展大都是存储在这里的。
demangledName
是计算机语言用于解决实体名称唯一性的一种方法,做法是向名称中添加一些类型信息,用于从编译器中向链接器传递更多语义信息
值得注意的是 method_array_t
的结构会有三种变化。
- 1️⃣:空。
- 2️⃣:一个一维数组的指针。(默认为一维数组,就算一个类有
category
,如果没有实现 +load
,就也是一维数组,没有实现 +load
,就意味这个类可以懒加载)
- 3️⃣:指向列表的指针数组。(当一个类存在
category
且这个类无法懒加载,或者动态修改方法列表如 addMethod
。就会生成二维列表,)
如果从二进制的角度讲,只要这个类存在于 __objc_catlist
或者 __objc_nlcatlist
,那么他的 method_array_t
就为二维数组。
那么 class_rw_t
是什么时候被赋值的呢。
三、类的初始化
3.1、realizeClass
首先简单看一个runtime初始化的流程。
类的可读写元数据的初始话主要发生在 realizeClass
和 methodizeClass
```c
static Class realizeClass(Class cls)
{
runtimeLock.assertLocked();
...
// fixme verify class is not in an un-dlopened part of the shared cache?
//RO_FUTURE 标志位来记录是否可以直接强转。
ro = (const class_ro_t *)cls->data();
if (ro->flags & RO_FUTURE) {
// This was a future class. rw data is already allocated.
rw = cls->data();
ro = cls->data()->ro;
cls->changeInfo(RW_REALIZED|RW_REALIZING, RW_FUTURE);
} else {
//不能转换,则开辟空间。
// Normal class. Allocate writeable class data.
rw = (class_rw_t *)calloc(sizeof(class_rw_t), 1);
rw->ro = ro;
rw->flags = RW_REALIZED|RW_REALIZING;
cls->setData(rw);
}
isMeta = ro->flags & RO_META;
rw->version = isMeta ? 7 : 0; // old runtime went up to 6
...
优先执行supercls 和metacls 的realize
检测isa 优化
更新实例大小,关联子类父类,元类根类。
...