本文已经默认你已经了解了实例对象
、类对象
和元类对象
,关于什么是实例对象
、类对象
和元类对象
,请自行了解不在本文的范围内。
*前言
平时开发的过程中我们会创建类,然后给这个类增加属性,方法,甚至添加协议和分类,然后分类有添加方法等等,那么这些添加的东西在内存中是如何存放的呢,当我们调用某个方法的时候又是如何完成调用的呢?
*准备工作
创建一个command line tool
项目, 编辑main.m
内容如下
1 | #import <Foundation/Foundation.h> |
将代码通过clang
命令重写成 架构为arm64的c++
代码
1 | xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m |
会在main.m
同层目录下生成main.cpp
文件
*3中对象的生成
查看main.cpp
文件会看到三个核心的对象,分别对象实例对象
、类对象
和元类对象
实例对象
1 | struct Animal_IMPL { |
类对象
,代码做了适量的删减
1 | struct _class_t OBJC_CLASS_$_Animal __attribute__ ((used, section ("__DATA,__objc_data"))) = { |
元类对象
,代码做了适量的删减
1 | struct _class_t OBJC_METACLASS_$_Animal __attribute__ ((used, section ("__DATA,__objc_data"))) = { |
*实例对象职责
由main.m
函数的中Animal
类的代码可以知道,我们一共给Animal
对象增加了以下内容
共有成员变量4个
1 | int _age; |
属性1个
1 | @property (nonatomic, copy) NSString *testProperty; |
实例方法1个
1 | - (int)testFunction:(int)a; |
类方法1个
1 | + (int)testClassFunction; |
由实例对象的结构可以知道
1 | struct Animal_IMPL { |
这个对象拥有了4个成员变量和一个_testProperty
的属性,除了一个父类的结构体之外并无其他,得出以下结论
- 实例对象只存放成员变量;
- 属性会生成
_
+属性名称
的成员变量 (这也进一步说明@property会生成_的成员变量)
至此成员变量已经有人认领了—> 实例对象
;
*类对象职责
由类对象的数据结构可知是为_class_t
类型的
1 | struct _class_t { |
再看下类对象
的结构
1 | struct _class_t OBJC_CLASS_$_Animal __attribute__ ((used, section ("__DATA,__objc_data"))) = { |
类对象的ro
指向_OBJC_CLASS_RO_$_Animal
地址,结构如下
1 | static struct _class_ro_t _OBJC_CLASS_RO_$_Animal __attribute__ ((used, section ("__DATA,__objc_const"))) = { |
可以知道是_class_ro_t
类型的 _class_ro_t
结构如下
1 | struct _class_ro_t { |
点击_OBJC_$_INSTANCE_METHODS_Animal
如下
1 |
|
装着所有的实例方法(也可以看出@property会生成对应的set和get方法)。
_objc_method
结构如下
1 | struct _objc_method { |
至此,实例方法有人认领了—> 类对象
;
成员变量
指向_OBJC_$_INSTANCE_VARIABLES_Animal
1 | static struct /*_ivar_list_t*/ { |
_ivar_t
结构如下
1 | struct _ivar_t { |
这里都是对于成员变量的描述信息
属性变量
指向_OBJC_$_PROP_LIST_Animal
1 | static struct /*_prop_list_t*/ { |
_prop_t
结构如下
1 | struct _prop_t { |
发现只有testProperty
一个属性,简单说下
1 | {{"testProperty","T@\"NSString\",C,N,V_testProperty"}} |
1 | T@"NSString":表示这个属性的类型是 NSString 型 |
结论
成员变量和属性变量还是有区别的 成员变量包含属性变量
只剩下类方法无人认领了,猜也猜到了—> 元类对象
*元类对象职责
由下面代码可以知道METACLASS
也是_class_t
类型的,只不过_class_ro_t
指向的是_OBJC_METACLASS_RO_$_Animal
的地址
1 | struct _class_t OBJC_METACLASS_$_Animal __attribute__ ((used, section ("__DATA,__objc_data"))) = { |
_OBJC_METACLASS_RO_$_Animal 如下
1 | static struct _class_ro_t _OBJC_METACLASS_RO_$_Animal __attribute__ ((used, section ("__DATA,__objc_const"))) = { |
OBJC$_CLASS_METHODS_Animal 如下
1 | static struct /*_method_list_t*/ { |
到此为止类
方法也有人认领了—> 元类对象
也可以看出类对象
和元类对象
的数据结构是一样的,只不过放的东西不一样罢了
*3中对象如何关联的
实例对象
1 | struct Animal_IMPL { |
类对象
,代码做了适量的删减
1 | struct _class_t OBJC_CLASS_$_Animal __attribute__ ((used, section ("__DATA,__objc_data"))) = { |
元类对象
,代码做了适量的删减
1 | struct _class_t OBJC_METACLASS_$_Animal __attribute__ ((used, section ("__DATA,__objc_data"))) = { |
有必要把上面的代码再写一遍,3中对象的数据结构以及知道了,那这三个是如何关联起来的呢,对于类对象
和元类对象
在内存里面只有一份,编译阶段就可以初始化了
1 | static void OBJC_CLASS_SETUP_$_Animal(void ) { |
而对于实例对象
,可以有很多多个,只能等到初始化的时候才能指定了
1 | inline void |
到这三者已经串联起来了
什么是isa不是本文的重点
最后会有个对外输出的指针供运行时调用
1 | static struct _class_t *L_OBJC_LABEL_CLASS_$ [1] __attribute__((used, section ("__DATA, __objc_classlist,regular,no_dead_strip")))= { |
*各种情况的调用
获取属性:通过实例对象
拿的;
获取对象方法:实例对象
通过isa
找到对应实例的类对象
拿的;
获取类方法:实例对象
通过isa
找到对应实例的类对象
拿的;
遗留问题
协议
、分类
方法没有分析,读者可自行分析,后面我也会输出文章;- 所有的这些属性存进去了,能不能获取到呢;
- 对外输出的指针如何被runtime调用的;