iOS--oc对象,类,和元类本质

iOS--oc对象,类,和元类本质

前言

最近在学习runtime的过程中,发现其中消息发送-动态方法解析-消息转发中涉及到了大量的类与对象的底层知识,看别人博客的时候,别人往往也会先讲一遍oc对象的本质 ;

具体参考了iOS底层原理总结 - 探寻OC对象的本质

实例对象的具体结构

在探讨oc对象的本质前,首先我们要明白oc语言他的底层实现都是c/c++代码 ;

如图:

oc中的对象,在c/c++中往往以结构体的形式存在 ;

NSobject对象如下:

objectivec 复制代码
struct NSObject_IMPL {
	Class isa;
};
// 查看Class本质
typedef struct objc_class *Class;
我们发现Class其实就是一个指针,对象底层实现其实就是这个样子。

也就是说Nsobject结构体中只存有一个isa指针,至于这个指针指向哪里,其实我们也可以大致猜到了,这个isa指针指向了类(结构体对象);

这里注意一个点,这里的isa指针在arm64架构前是一个单纯的指针,但在arm64架构优化指针后底层是一个联合图或者说共用体(union);这里的isa使用的共用体为了节省空间,不断的进行值覆盖的操作,结合位域可以更大限度的节约内存空间,还不用覆盖旧值 ;

其中的具体细节就不说了,只需要知道优化后的共用体不仅存放这类对象或元类对象地址,还存放了很多额外属性 ;

  • 还有,指针在64位架构中占8个字节;
  • 在Objective-C中,对象的地址确实可以视为指向其内部isa指针的地址,因为isa是对象内存布局的第一个元素。所以,当你说"objc存储的就是isa的地址",这一点是对的。

自定义类对象的结构

这里用别人的一个例子:

objectivec 复制代码
@interface Student : NSObject{
    
    @public
    int _no;
    int _age;
}
@end
@implementation Student

int main(int argc, const char * argv[]) {
    @autoreleasepool {

        Student *stu = [[Student alloc] init];
        stu -> _no = 4;
        stu -> _age = 5;
        
        NSLog(@"%@",stu);
    }
    return 0;
}
@end

这里中的对象的结构体可以转换为如下的形式:

objectivec 复制代码
struct Student_IMPL {
	Class *isa;
	int _no;
	int _age;
};

此结构体占用多少存储空间,对象就占用多少存储空间。因此结构体占用的存储空间为,isa指针8个字节空间+int类型_no4个字节空间+int类型_age4个字节空间共16个字节空间;

具体内存大小的分析记得要内存对齐 ;

那么一个NSObject对象占用多少内存? NSObjcet实际上是只有一个名为isa的指针的结构体,因此占用一个指针变量所占用的内存空间大小,如果64bit占用8个字节,如果32bit占用4个字节。

继承关系

objectivec 复制代码
/* Person */
@interface Person : NSObject
{
    int _age;
}
@end

@implementation Person
@end

/* Student */
@interface Student : Person
{
    int _no;
}
@end

@implementation Student
@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
        NSLog(@"%zd  %zd",
              class_getInstanceSize([Person class]),
              class_getInstanceSize([Student class])
              );
    }
    return 0;
}

类对象实质上是以结构体的形式存储在内存中;下面是类对象结构体的图示:

注意一下,上面的类对象结构体是不完整的,应该是一种简化版本 ;

  • 我们发现只要是继承自NSObject的对象,那么底层结构体内一定有一个isa指针。

那么他们所占的内存空间是多少呢?单纯的将指针和成员变量所占的内存相加即可吗?上述代码实际打印的内容是16 16,也就是说,person对象和student对象所占用的内存空间都为16个字节。

其实实际上person对象确实只使用了12个字节。但是因为内存对齐的原因。使person对象也占用16个字节。

类信息的存放

从实例对象的结构出发,我们知道了它的结构体中只有isa指针和成员变量 ;
instance对象就是通过类alloc出来的对象,每次调用alloc都会产生新的instance对象;

instance对象在内存中存储的信息包括

  • isa指针
  • 其他成员变量

既然instance对象结构体中不包括类的方法信息 ;那我们该如何访问类的方法信息 ?

class对象 我们通过class方法或runtime方法得到一个class对象。class对象也就是类对象

每一个类在内存中有且只有一个class对象。

class对象在内存中存储的信息主要包括

  • isa指针
  • superclass指针
  • 类的属性信息(@property),类的成员变量信息(ivar)
  • 类的对象方法信息(instance method),类的协议信息(protocol)

所以instance对象的isa指针指向class对象来访问方法信息 ;

成员变量的值时存储在实例对象中的,因为只有当我们创建实例对象的时候才为成员变赋值。但是成员变量叫什么名字,是什么类型,只需要有一份就可以了。所以存储在class对象中。

同样的class对象的isa指针指向的是它的元类对象 ;

元类对象 meta-class

在内存中存储的信息主要包括

  • isa指针
  • superclass指针
  • 类的类方法的信息(class method)

meta-class对象和class对象的内存结构是一样的,所以meta-class中也有类的属性信息,类的对象方法信息等成员变量,但是其中的值可能是空的。
class的isa指向meta-class 当调用类方法时,通过class的isa找到meta-class,最后找到类方法的实现进行调用

1.当对象调用实例方法的时候,我们上面讲到,实例方法信息是存储在class类对象中的,那么要想找到实例方法,就必须找到class类对象,那么此时isa的作用就来了。

2.当类对象调用类方法的时候,同上,类方法是存储在meta-class元类对象中的。那么要找到类方法,就需要找到meta-class元类对象,而class类对象的isa指针就指向元类对象

3.当对象调用其父类对象方法的时候,又是怎么找到父类对象方法的呢?,此时就需要使用到class类对象superclass指针。

当Student的instance对象要调用Person的对象方法时,会先通过isa找到Student的class,然后通过superclass找到Person的class,最后找到对象方法的实现进行调用,同样如果Person发现自己没有响应的对象方法,又会通过Person的superclass指针找到NSObject的class对象,去寻找响应的方法

这里我猜想可能与runtime的消息流程有关,先从isa指针寻找,找不到就寻找到父类中,然后从父类的isa指针寻找 ;下面寻找父类的类方法也是一样的 ;

4.当类对象调用父类的类方法时,就需要先通过isa指针找到meta-class,然后通过superclass去寻找响应的方法

对isa、superclass总结

  • instance的isa指向class
  • class的isa指向meta-class
  • meta-class的isa指向基类的meta-class,基类的isa指向自己
  • class的superclass指向父类的class,如果没有父类,superclass指针为nil
  • meta-class的superclass指向父类的meta-class,基类的meta-class的superclass指向基类的class
  • instance调用对象方法的轨迹,isa找到class,方法不存在,就通过superclass找父类
  • class调用类方法的轨迹,isa找meta-class,方法不存在,就通过superclass找父类
相关推荐
Swift社区7 小时前
iOS 基于 Foundation Model 构建媒体流
ios·iphone·swift·媒体
上去我就QWER9 小时前
macOS下如何全文检索epub格式文件?
macos
库奇噜啦呼9 小时前
【iOS】音频与视频播放
ios·音视频·cocoa
谢娘蓝桥10 小时前
Mac 安装 Xcode 及qt 环境安装
ide·macos·xcode
witton13 小时前
克隆整个macOS系统到新磁盘
macos·asr·克隆·复制·迁移·恢复·restore
csdn_aspnet15 小时前
如何在 Mac、Ubuntu、CentOS、Windows 上安装 MySQL 客户端
linux·windows·mysql·macos·centos
我的golang之路果然有问题15 小时前
mac M系列芯片 unity 安装会遇到的错误以及解决
经验分享·学习·macos·unity·游戏引擎
大熊猫侯佩17 小时前
黑衣人档案:用 Apple Foundation Models + SwiftUI 打造 AI 聊天机器人全攻略
ios·swiftui·ai编程
大熊猫侯佩17 小时前
侠客行・iOS 26 Liquid Glass TabBar 破阵记
ios·swiftui·swift
心灵宝贝17 小时前
如何安装 PDF Reader Pro for Mac v3.2.1.0?超简单DMG安装步骤(附安装包)
macos