总结
- 结构体内存对齐: 结构体内存的大小必须是最大成员内存大小的倍数
- 操作系统有分配内存对齐的概念(Bucket Size), 大都以16个字节为单位对齐
- 在iOS堆空间中分配内存的话都是16的倍数,也就是说iOS堆内存中最小内存的大小为16, 这样对于操作系统访问CPU是最优最快的。(如果结构体的成员变量只需要24个字节就够用,而iOS中分配堆内存大小是32个字节)
- class_getInstanceSize( )返回某个类对象至少需要多少空间
- malloc_size()返回的是实例对象实际分配的内存空间
一、面向对象
1.1 Objective-C的本质
-
我们平时编写的Objective-C代码,底层实现其实都是C\C++代码
-
所以Objective-C的面向对象都是基于C\C++的数据结构实现的
-
思考:Objective-C的对象、类主要是基于C\C++的什么数据结构实现的?
- 结构体
-
将Objective-C代码转换为C\C++代码
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc OC源文件 -o 输出的CPP文件
- 如果需要链接其他框架,使用-framework参数。比如-framework UIKit
1.2 OC对象的本质
- 思考:一个OC对象在内存中是如何布局的?
- NSObject的底层实现
- 一个普通类的底层实现
- 思考:一个Person对象、一个Student对象占用多少内存空间?
1.3 实时查看内存数据
- Debug -> Debug Workfllow -> View Memory (Shift + Command + M)
- 也可以使用LLDB指令
1.4 2个容易混淆的函数
- 创建一个实例对象,至少需要多少内存?
#import <objc/runtime.h>
class_getInstanceSize([NSObject class]);
- 创建一个实例对象,实际上分配了多少内存?
#import <malloc/malloc.h>
malloc_size((__bridge const void *)obj);
二、常用LLDB指令
-
print、p : 打印
-
po : 打印对象
-
读取内存
memory read/数量格式字节数 内存地址
x/数量格式字节数 内存地址
x/3xw 0x10010
- 修改内存中的值
memory write 内存地址 数值
memory write 0x0000010 10
-
格式
- x是16进制,f是浮点,d是10进制
-
字节大小
- b : byte 1字节,
- h : half word 2字节
- w : word 4字节,
- g : giant word 8字节
三、OC对象的分类
- Objective-C中的对象,简称OC对象,主要可以分为3种
- instance对象(实例对象)
- class对象(类对象)
- meta-class对象(元类对象)
3.1 instance
- instance对象就是通过类alloc出来的对象,每次调用alloc都会产生新的instance对象
NSObject *object1 = [[NSObject alloc] init];
NSObject *object2 = [[NSObject alloc] init];
-
object1、object2是NSObject的instance对象(实例对象)
-
它们是不同的两个对象,分别占据着两块不同的内存
-
instance对象在内存中存储的信息包括
- isa指针
- 其他成员变量
-
Person对象
@interface MJPerson : NSObject {
@public
int _age;
}
@end
- Person对象使用
MJPerson *p1 = [[MJPerson alloc] init];
p1->_age = 3;
MJPerson *p2 = [[MJPerson alloc] init];
p2->_age = 4;
3.2 class
- objectClass1 ~ objectClass5都是NSObject的class对象(类对象)
- 它们是同一个对象。每个类在内存中有且只有一个class对象
ClassobjectClass1 = [object1 class];
Class objectClass2 = [object2 class];
Class objectClass3 = object_getClass(object1); // Runtime API
Class objectClass4 = object_getClass(object2); // Runtime API
Class objectClass5 = [NSObject class];
- class对象在内存中存储的信息主要包括
- isa指针
- superclass指针
- 类的属性信息(@property)、类的对象方法信息(instance method)
- 类的协议信息(protocol)、类的成员变量信息(ivar)
- …
3.3 meta-class
Class objectMetaClass = object_getClass(objectClass5);
-
objectMetaClass是NSObject的
meta-class
对象(元类对象) -
每个类在内存中有且只有一个meta-class对象
-
meta-class对象和class对象的内存结构是一样的,但是用途不一样,在内存中存储的信息主要包括
isa
指针superclass
指针- 类的
类方法
信息(class method) - …
3.4 注意
- 以下代码获取的objectClass是class对象,并不是meta-class对象
Class objectMetaClass2 = [[[NSObject class] class] class];
3.5 查看Class是否为meta-class
#import <objc/runtime.h>
BOOL result = class_isMetaClass([NSObject class]);
四、isa 和 superclass
4.1 isa指针
-
instance的isa指向class
- 当调用对象方法时,通过instance的isa找到class,最后找到对象方法的实现进行调用
-
class的isa指向meta-class
- 当调用类方法时,通过class的isa找到meta-class,最后找到类方法的实现进行调用
4.2 class对象的superclass指针
- 当Student的instance对象要调用Person的对象方法时
- 会先通过isa找到Student的class
- 然后通过superclass找到Person 的class
- 最后找到对象方法的实现进行调用
4.3 isa、superclass总结
-
instance的isa指向class
-
class的isa指向meta-class
-
meta-class的isa指向基类的meta-class
-
class的superclass指向父类的class p 如果没有父类,superclass指针为nil
-
meta-class的superclass指向父类的meta-class p 基类的meta-class的superclass指向基类的class
-
instance调用对象方法的轨迹
- isa找到class,方法不存在,就通过superclass找父类
-
class调用类方法的轨迹
- isa找meta-class,方法不存在,就通过superclass找父类
五、Class的结构
5.1 isa指针
- 从64bit开始,isa需要进行一次位运算,才能计算出真实地址
5.2 objc4源码下载
5.3 窥探struct objc_class的结构
- class、meta-class对象的本质结构都是struct objc_class
5.4 objc_getClass和object_getClass的区别
-
Class objc_getClass(const char *aClassName)
1> 传入字符串类名
2> 返回对应的类对象 -
Class object_getClass(id obj)
1> 传入的obj可能是instance对象、class对象、meta-class对象
2> 返回值- a) 如果是instance对象,返回class对象
- b) 如果是class对象,返回meta-class对象
- c) 如果是meta-class对象,返回NSObject(基类)的meta-class对象
-
- (Class)class、+ (Class)class
1> 返回的就是类对象
- (Class) {
return self->isa;
}
+ (Class) {
return self;
}