经过对小码哥底层原理视频学习,通过以下面试题来分析oc对象的本质
1.一个NSObject对象占用多少内存?
我们平时编写的Objective-C代码,底层实现其实都是C\C++代码
所以objective-c的面向对象都是基于C\C++的数据结构实现的
思考:objective-c的对象、类主要是基于C\C++的什么数据结构实现的?
结构体
接下来我们通过将oc的代码转换为c++的代码来进一步剖析oc代码编译成c++代码内部是怎样的
oc代码如下
#import
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSObject *obj = [[NSObject alloc] init];
}
return 0;
}
终端通过命令行将main.h转化为c++文件
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main-arm64.cpp
生成 main-arm64.cpp
思考:一个OC对象在内存中是如何布局的?
NSObject的底层实现
在生成的main-arm64.cpp中搜索NSObjcet_IMPL看nsobject内部实现
struct NSObject_IMPL {
Class isa;//
};
Class里面的为
typedef struct objc_class *Class;
通过看它的内部实现其实不难发现就是一个结构体,结构体里面存了一个isa指针
NSObject *obj = [[NSObject alloc] init];
那么这个nsobject对象占多大内存空间呢?
我们可以通过两个方法来获得
- class_getInstanceSize([NSObject class]);//创建一个实例对象至少需要多少内存空间 8字节
- malloc_size((__bridge const void *)obj);//创建一个实例对象系统分配了多少内存空间 16字节
因为isa指针在64bit下是8个字节,而iOS手机端在64bit下系统分配的内存是16的整数倍,最少是16个字节,所以综上得出第一个面试题答案
1.一个NSObject对象占用多少内存?
系统分配了16个字节给NSObject对象(通过malloc_size函数获得)
但NSObject对象内部只使用了8个字节的空间(64bit环境下,可以通过class_getInstanceSize函数获得)
存在继承关系的自定义类的占多少内存呢
思考:一个Person对象、一个Student对象占用多少内存空间?
#import
#import
#import
@interface Person : NSObject
{
@public
int _age;
}
@end
@implementation Person
@end
@interface Student : Person
{
@public
int _no;
}
@end
@implementation Student
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person *person = [[Person alloc] init];
Student *stu = [[Student alloc] init];
NSLog(@"person-%zd",malloc_size((__bridge const void *)(person)));
NSLog(@"stu-%zd",malloc_size((__bridge const void *)(stu)));
}
return 0;
}
他们的底层内存结构如下图所示
运行结果Person的内存占用16字节, Student的内存占用为16字节
这里涉及一个知识点,struct内存对齐原则,我们这大致遵守2条原则就好
- 结构体的总大小必须是内部最大成员的整数倍
- 数据成员对齐规则:第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员大小的整数倍开始
peron的内存大小8+4+(4) = 16 //括号内就是内存对齐补齐的字节数
student的内存大小 8+ 4+4 = 16
实时查看内存数据
方法一
方法二
通过LLDB指令
OC对象的分类
Objective-C中的对象,简称OC对象,主要可以分为以下3类
- instance对象(实例对象)
- class对象(类对象)
- meta-class对象(元类对象)
instance对象
instance对象就是通过类alloc出来的对象,每次调用alloc都会产生新的instance对象
NSObject *obj1 = [[NSObject alloc] init]; NSObject *obj2 = [[NSObject alloc] init];
obj1、obj2是NSObject的instance对象,它们是不同的两个对象,分别占据着两块不同的内存
下面看下自定义类Person的实例对象在内存中存储的信息包括
- isa指针
- 其他成员变量
@interface Person : NSObject
{
@public
int _age;
}
@end
Person *p1 = [[Person alloc] init];
Person *p2 = [[Person alloc] init];
class对象(类对象)
NSObject *obj1 = [[NSObject alloc] init];
NSObject *obj2 = [[NSObject alloc] init];
Class ocClass1 = [obj1 class];
Class ocClass2 = [obj2 class];
Class ocClass3 = [NSObject class];
Class ocClass4 = object_getClass(obj1);
Class ocClass5 = object_getClass(obj2);
ocClass1~ocClass5都是NSObject的class对象(类对象)
它们是同一个对象,每个类在内存中有且只有一个class对象
class对象在内存中存储的信息主要包括
- isa指针
- superClass指针
- 对象方法信息
- 成员变量信息
- 属性信息
-
类的协议信息
meta-class对象(元类对象)
Class objectMetaClass = object_getClass([NSObject class]);// runtime
API
objectMetaClasss是NSObject的meta-class对象(元类对象)
每个类在内存中有且只有一个meta-class对象
meta-class对象和class对象的内存结构是一样的,但是用途不一样,在内存中储存的信息主要包括
- isa指针
- superclass指针
-
类方法信息
注意:以下代码获取的objectClass是class对象,并不是meta-class对象
Class objectClass = [[NSObject class] class];
class方法无论调用多少次都是获取到的类class对象
判断一个NSObject的类对象是否是元类对象
BOOL result = class_isMetaClass([NSObject class]);
isa指针
- instan对象的isa指向class
当调用对象方法时,通过instance的isa找到class,最后找到对象方法的实现进行调用 - class的isa指向meta-class
当调用类方法时,class的isa找到meta-class,最后找到类方法的实现进行调用
class对象的superclass指针
@interface Person : NSObject
@interface Student : Person
-
当Student的instance对象要调用Person的对象方法时,会先通过isa找到Student的class,因为Student继承自Person,Student的class里的superclass会找到他的父类的类对象(Person的class),最后再调用它里面的对象方法的实现进行调用
- 当Student的class要调用Person的类方法时,会先通过isa找到Student的meta-class,然后通过superclass找到Person的meta-class,最后找到类方法的实现进行调用
isa、superclass总结
instance的isa指向class
class的isa指向meta-class
meta-class的isa指向基类的meta-class
class的superclass指向父类的class
如果没有父类,superclass指针为nilmeta-class的superclass指向父类的meta-class
基类的meta-class的superclass指向基类的classinstance调用对象方法的轨迹
isa找到class,方法不存在,就通过superclass找父类-
class调用类方法的轨迹
isa找meta-class,方法不存在,就通过superclass找父类
从64bit开始,isa需要进行一次位运算,才能计算出真实的地址
class、meta-class对象的本质结构都是struct objc_class
本章知识点对应的面试题 一个NSObject对象占用多少内存?
系统分配了16个字节给NSObject对象(通过malloc_size函数获得)
但NSObject对象内部只使用了8个字节的空间(64bit环境下,可以通过class_getInstanceSize函数获得)对象的isa指针指向哪里?
instance对象的isa指向class对象
class对象的isa指向meta-class对象
meta-class对象的isa指向基类的meta-class对象OC的类信息存放在哪里?
对象方法、属性、成员变量、协议信息,存放在class对象中
类方法,存放在meta-class对象中
成员变量的具体值,存放在instance对象