iOS底层-OC对象的本质

经过对小码哥底层原理视频学习,通过以下面试题来分析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];
obj在内存中布局

那么这个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;
}

他们的底层内存结构如下图所示


oc转换成c++

运行结果Person的内存占用16字节, Student的内存占用为16字节
这里涉及一个知识点,struct内存对齐原则,我们这大致遵守2条原则就好

  1. 结构体的总大小必须是内部最大成员的整数倍
  2. 数据成员对齐规则:第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员大小的整数倍开始
    peron的内存大小8+4+(4) = 16 //括号内就是内存对齐补齐的字节数
    student的内存大小 8+ 4+4 = 16

实时查看内存数据

方法一

通过xcode菜单

方法二
通过LLDB指令
常见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];
p1、p2在内存中存储信息

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指针
  • 对象方法信息
  • 成员变量信息
  • 属性信息
  • 类的协议信息


    class对象在内存中的存储信息

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指针

isa指针指向
  • instan对象的isa指向class
    当调用对象方法时,通过instance的isa找到class,最后找到对象方法的实现进行调用
  • class的isa指向meta-class
    当调用类方法时,class的isa找到meta-class,最后找到类方法的实现进行调用

class对象的superclass指针

@interface Person : NSObject
@interface Student : Person
superclass指向
  • 当Student的instance对象要调用Person的对象方法时,会先通过isa找到Student的class,因为Student继承自Person,Student的class里的superclass会找到他的父类的类对象(Person的class),最后再调用它里面的对象方法的实现进行调用


    截屏2021-02-05 上午11.09.34.png
  • 当Student的class要调用Person的类方法时,会先通过isa找到Student的meta-class,然后通过superclass找到Person的meta-class,最后找到类方法的实现进行调用

isa、superclass总结

截屏2021-02-05 上午11.11.01.png
  • instance的isa指向class

  • class的isa指向meta-class

  • meta-class的isa指向基类的meta-class

  • class的superclass指向父类的class
    如果没有父类,superclass指针为nil

  • meta-class的superclass指向父类的meta-class
    基类的meta-class的superclass指向基类的class

  • instance调用对象方法的轨迹
    isa找到class,方法不存在,就通过superclass找父类

  • class调用类方法的轨迹
    isa找meta-class,方法不存在,就通过superclass找父类
    从64bit开始,isa需要进行一次位运算,才能计算出真实的地址


    &ISA_MASK

    class、meta-class对象的本质结构都是struct objc_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对象

你可能感兴趣的:(iOS底层-OC对象的本质)