写写代码,聊聊生活

0%

Objective-C 对象

对象的本质

OC 对象本质上是通过 C/C++ 基于结构体来实现的, 平时我们定义的类,一般都继承自 NSObject

定义一个 Person 类

1
2
3
4
5
@interface Person : NSObject {
@public
int _age;
}
- (void)run;

通过以下命令可以将 OC 代码转成 C/C++ 代码

1
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc OC源文件 -o 输出的CPP文件

将 Person 类转换后,在 .cpp 文件中,可以看到 Person 类转换成了一个 Person_IMPL 结构体

1
2
3
4
5
6
7
struct Person_IMPL {
struct NSObject_IMPL NSObject_IVARS;
int _age;
};
struct NSObject_IMPL {
Class isa;
};

除了我们自己定义成员变量以外,还自动生成了一个 isa 指针,最终指向 objc_class 结构体,isa 指针有什么用呢?

实例对象的内存布局

创建一个对象,对象内存存储的是一个 isa 指针和定义的成员变量

NSObject 实例对象就只有一个 isa 指针,在64位下,一个指针占用8个字节,所以创建一个 NSObject 对象,至少是需要8个字节的,可以使用 runrime 提供的函数获得一个实例对象所需的内存

1
2
#import <objc/runtime.h>
class_getInstanceSize([NSObject class]); //8

但是由于存在内存对齐,在 Mac / iOS 系统上,实例对象在分配内存时大小是以16的倍数进行分配的,所以至少是16字节,可以通过以下函数获取实际占用的内存大小,以 NSObject 为例

1
2
#import <malloc/malloc.h>
malloc_size((__bridge const void *)obj); //16

对象的分类

OC对象主要可以三类

  • instance 对象(实例对象)
  • class 对象(类对象)
  • meta-class 对象(元类对象)

instance 对象

通过类alloc出来的对象,每次alloc产生的都是新对象,内存在堆中,内存中存储着 isa 指针和其他成员变量的值

class 对象

在 runtime 初始化时,会初始化 class 对象,内存在数据段中,全局只有一份,内存布局如图

获取类对象

1
2
[NSObject class] //通过类获取
object_getClass(obj) //通过实例对象获取

meta-class 对象

meta-class 其实是一种特殊的类对象,跟类对象的结构是一样的,但是用途不一样,内存在数据段中,全局只有一份,主要的内存布局如下

获取 meta-class 对象

1
object_getClass([NSObject class]) //通过类对象获取

isa 指针

OC三种对象都有 isa 指针,这个指针有什么用呢?

instance 对象的 isa 指针指向 class 对象

  • 当调用实例的对象方法时,通过 instance 的 isa 找到 class 对象,如果当前 class 对象没有对应方法,通过 superclass 指针找父类的 class 对象,最终找到方法并调用

class 对象的 isa 指针指向 meta-class 对象

  • 当调用类方法时,通过 class 对象的 isa 找到 meta-class 对象,如果当前 meta-class 对象没有找到对应方法,通过 superclass 指针找父类的 meta-class 对象,最终找到方法并调用

引用一张图片,可以清晰看出 isa 和 superclass 在对象中的引用关系

总结

  • OC对象本质是结构体,都有 isa 指针
  • 实例对象内存只存储 isa 和 成员变量的值
  • 类的信息,比如 superclass 指针,对象方法列表、属性信息等都是存放在类对象中的
  • meta-class 对象是一种特殊的类对象
  • 通过 isa 和 superclass 指针,把对象都串联起来了