文章目录
OC本质底层实现转化
OC本质底层实现转化其实都是C/C++代码
那么如何转化为C/C++代码
- 打开终端
$ cd 文件所在的路径
$ xcrun -sdk iphoneos clang -arch a m64 -rewrite-objc -fobjc-arc -fobjc-runtime=ios-8.0.0 main.m -o main-arm64.cpp
此时就会生成对应的C++文件
对象的本质
- 对象类的本质是结构体
@interface Person : NSObject
{
//成员变量
//@public
int _age; //4个字节
int _height;
int _no;
}
@end
//转化成C++
struct Person_IMPL {
struct NSObject_IMPL NSObject_IVARS;
int _age;
int _height;
int _no;
};
获取内存大小
- 类的实例对象的大小(本质是对象中成员变量的大小)
函数返回对齐过的实例变量(传入类)
class_getInstanceSize
- 获得系统实际分配的内存大小(也存在内存对齐),obj指针所指向内存的大小
malloc_size((__bridge const void *)obj)
- 针对类型(int/double/struct)是一个运算符,计算类型的大小(编译时确定数值)
//例如
int 4字节/(对象)指针 8字节/bool 2字节
//某个结构体类型的大小
sizeof(struct Person_IMPL)![ ](https://img-blog.csdnimg.cn/f06d318f21384e409deaa179deeb198b.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NTg0MTUyMg==,size_16,color_FFFFFF,t_70#pic_center)
问题:一个NSObject对象占用多少内存?
首先通过分析对象类的本质得出其本质其实是结构体,由于NSObject结构体中的isa本质上是一个指针,在64位中指针占8个字节,而通过malloc得出他的实际内存是16,剩下的8位没有用。
//NSOject本质
struct NSObject_IMPL {
__unsafe_unretained Class isa;
};
-
系统分配了16字节给NSObject对象(通过malloc_size函数获得)
-
但NSObject对象内部只使用了8字节的空间(64bit环境下,通过class_getInstanceSize函数获得,返回内存对齐后成员变量的大小)
小tip:结构体最终占用内存大小查看方法
- 命令打印
- p(print):打印 po:打印对象
- 读取内存
-
- memory read/数量 格式 字节数 内存地址
-
- x/数量 格式 字节数 内存地址
例:x/3xw 0x10010
- x/数量 格式 字节数 内存地址
- 格式
-
- x是16进制,f是浮点,d是10进制
-
- 字节大小:b:byte 1字节, h:half word 2字节, w:word 4字节, g:giant world 8字节
- 修改内存中的值
-
- memory write 内存地址 数值
例:memory write 0x0000010 10
- memory write 内存地址 数值
- 实时查看内存数据
16进制1个16进制位代表4个2进制位,2个16进制位就代表8个2进制位即1个字节
即占16个字节
有关内存对齐的知识
- 数据成员的对齐规则可以理解为
min(m, n)
的公式, 其中 m表示当前成员的开始位置, n表示当前成员所需要的位数。如果满足条件 m 整除 n (即m % n == 0
), n 从 m 位置开始存储, 反之继续检查 m+1 能否整除 n, 直到可以整除, 从而就确定了当前成员的开始位置。 - 数据成员为结构体:当结构体嵌套了结构体时,作为数据成员的结构体的自身长度作为外部结构体的最大成员的内存大小,比如结构体a嵌套结构体b,b中有char、int、double等,则b的自身长度为8
- 最后结构体的内存大小必须是结构体中最大成员内存大小的整数倍,不足的需要补齐。
OC对象的分类
内存对齐规则
-
instance实例对象,在内存中存储的信息有isa,成员变量的具体值,所以实例对象在内存中分配有不同的内存和存储空减
-
class类对象,每一个类在内存中有且只有一个class对象。里面存放有isa,superclass,属性,对象方法,协议和成员变量(注意:实例对象中存放的是成员变量的具体值,在这里存放的是成员变量信息,比如名称类型等)
-
mate-class元类对象,因为方法是一样的,所以一个类在内存中也是有且只有一个元类对象的,里面存放的有isa,superclass,类对象
综合前文提及的获取内存大小的方式class_getInstanceSize:
是采用8字节对齐,参照的对象的属性内存大小
malloc_size
:采用16字节对齐,参照的整个对象的内存大小,对象实际分配的内存大小必须是16的整数倍
内存对齐原理
//instance实例对象
NSObject *object1 = [[NSObject alloc] init];
NSObject *object2 = [[NSObject alloc] init];
//class对象,类对象
//objectClass1~5都是NSObject的类对象
Class objectClass1 = [object1 class];
Class objectClass2 = [object2 class];
Class objectClass3 = [NSObject class];
Class objectClass4 = object_getClass(object1);
Class objectClass5 = object_getClass(object2);
//元类对象(将类对象当作参数传入进去)
Class objectMetaClass = object_getClass([NSObject class]);
Class objectMetaClass2 = [[NSObject class] class];
//判断是不是元类对象
NSLog(@"instance - %p %p", object1, object2);
NSLog(@"class - %p %p %p %p %p %d", objectClass1,objectClass2, objectClass3, objectClass4, objectClass5, class_isMetaClass(objectClass3 ));
NSLog(@"mateClass - %p %p %d",objectMetaClass, objectMetaClass2, class_isMetaClass(objectMetaClass));
输出情况:
instance - 0x100511920 0x10050e840
class - 0x7fff91da2118 0x7fff91da2118 0x7fff91da2118 0x7fff91da2118 0x7fff91da2118 0
mateClass - 0x7fff91da20f0 0x7fff91da2118 1
在上面例子中 我们可以得出:
- 实例对象通过创建分别有不同的两个
- class对象有很多表达方法,objectClass1~5都是NSObject的类对象
- 通过
object_getClass
得到元类对象,而且证明无论多少次class方法得到的都还是类函数。
小tips
Class object_getClass(id _Nullable obj)
传入的参数可能是instance对象,class对象或者元类对象
参数为实例对象,返回类对象
参数为类对象,返回元类对象
参数为元类对象,返回NSObject(基类)Class objc_getClass(const char *aClassName)
传入字符串类名
返回对应的类对象- (Class)class, +(Class)class
返回的就是类对象
对象的isa指针指向哪里
instance对象的isa指向class对象
class对象的isa指向meta-class对象
mate-class对象的isa指向基类的mate-class对象
对象的superClass指针指向哪里
Class对象的superClass指针
Person类继承自NSObject类,Student类继承自Person类
当student的instance对象要调用person的对象方法时,会先通过isa指针找到Student的class,然后通过superClass找到Person的class,最后找到对象方法的实现进行调用
meta-class对象的superclass指针
Student的class类调用Person的类方法时,会先通过isa找到Student的meta-class,然后通过superClass找到Person的mate-class,最后找到类方法的实现进行调用
isa,superclass总结
- instance对象的isa指向class对象
- class对象的isa指向meta-class对象
- mate-class对象的isa指向基类的mate-class对象
- class的superclass指向父类的class
如果没有父类,superclass指针为nil - meta-class的superclass指向父类的meta-class
基类的meta-class的superclass指向基类的class
通过类方法的方式调用NSObject类里的实例方法
类或元类对象的真实地址
实际上,在64bit开始,isa需要进行一次位运算即 isa & ISA_MASK,才能计算出指向的类或元类对象的真实地址
struct objc_class的结构
简化形式
struct objc_class {
// Class ISA;
Class superclass;
cache_t cache; // 方法缓存 formerly cache pointer and vtable
class_data_bits_t bits; // 用于获取具体的类信息 class_rw_t * plus custom rr/alloc flags
}
bits里面有class_rw_t,class_ro_t两个方法
-
对class_rw_t的理解
rw代表可读可写
类中的属性、方法、协议等信息都保存在class_rw_t中 -
对class_ro_t的理解
ro代表只读
存储了当前类在编译期就确定了的属性、方法、协议