iOS对象的底层探索(上)
2024-02-01 17:06:40
对于许多开发者来说,每天开发最常敲的代码就是alloc、init来进行对象的创建。
然而,我们都只是将alloc、init当作系统提供的接口,殊不知alloc和init只是客户端与系统的一个契约,底层的系统究竟是如何实现对象的创建呢?
alloc与init到底是如何进行数据布局的?
与objc对象一生的追随者引用计数是如何维护的?
我们日常使用的autoreleasepool又是如何与alloc和init进行协作,达成自动释放对象的使命的呢?
带着这些问题,我们将对iOS对象的底层实现进行详细的探索。
开始探索
我们先从alloc说起,苹果为我们提供了很多动态创建对象的方式,但是追溯其根源,创建对象的本质还是alloc、init。
那么,让我们用一个简单的例子,来探索一下iOS的alloc到底是做了什么呢?
- (id)new {
id obj = [[self alloc] init];
return obj;
}
这是我们在日常开发中,创建一个对象的代码。那么我们可以在运行时对这段代码进行动态分析,对创建对象的细节进行调查。
可以看出,在程序执行 alloc 的时候,会调用 objc_msgSend 函数,找到IMP进行调用。这里IMP进行的调用是 objc_msgSendSuper,这个API会对self的父类进行查找,如果父类还有父类就继续查找,知道找到根类,也就是NSObject。
当IMP对NSObject进行最终调用的时候,就会执行 allocWithZone:API,而allocWithZone是NSObject的类方法,通过最终的 objc_msgSend,找到了 NSObject 的实现:
id objc_msgSendSuper(struct objc_super *super, SEL op, ...) {
IMP imp = objc_msgSend(super->receiver, super->class, op, nil);
return imp(super, super->class, op, ...);
}
- (id)allocWithZone:(NSZone *)zone {
// 尝试保留需要的内存大小
id obj = NSAllocateMemoryPages(sizeof(MyClass));
if (obj == nil) {
return nil;
}
//为对象内存的开头部分初始化isa指针,使其指向类的对象实例结构体(class structure)
((__strong id *)obj)[0] = (id)&__objc_empty_vtable;
return obj;
}
在allocWithZone API 的实现中,可以看出,首先会根据类的大小去申请内存。
然后将分配的内存的首地址处用isa指针,指向类的对象实例结构体。
类对象,也叫类元类,它包括了类的元数据,也就是保存了类的信息,它在设计的时候就是用一个结构体进行保存,那么这个isa指针指向的就是这个结构体。
初步认识objc对象
无论是allocWithZone还是alloc,最终都是调用NSAllocateMemoryPages去申请内存,从而完成objc对象的创建
也就是说,所有的objc对象都是由NSAllocateMemoryPages来完成其内存的申请的,不过,对于objc对象来说,他们仅仅申请了内存还不行,还需要保证这个内存是连续的。
所以,在创建objc对象的时候,会使用内存分配器NSAllocateMemoryPages,来申请一块连续的内存空间,用于存放objc对象的数据。
当objc对象在创建成功后,就会将isa指针指向objc的class structure,而isa指针的内存地址就是objc对象的内存地址。
通过isa指针就可以获取到其指向的class structure,也就是objc对象的元数据。
在class structure中,会包含其super class和isa,也就是父类的isa和自己的isa,这种父类和子类的相互引用,就可以保证objc对象的继承性。
objc对象的底层本质其实就是一块连续的内存空间。
而这块连续内存空间中,最关键的两个内存区域就是isa指针和class structure。
ObjC对象的底层结构
**---------------------------- ObjC对象结构 ----------------------------**
| 起始地址 | isa指针 | class structure | 其他成员变量 |
|----------------|-------------|---------------------|------------------|
| 内存地址 | 类元类地址 | 类元数据地址 | 成员变量地址 |
|----------------|-------------|---------------------|------------------|
class NSObject {
Class isa;
};
iOS系统使用isa指针,将objc对象和类进行关联。
isa指针的地址就是该对象的内存地址,因此,通过一个对象的isa指针,就可以找到这个对象的类,进而从类中获取其成员变量、方法等信息。
而isa指针在objc中,就叫做class instance variable,其实就是class的一个成员变量。
我们可以利用class instance variable的特殊性,将objc对象和类进行关联。
当类发生改变时,关联的objc对象也会随着发生改变,这样一来,我们就可以通过isa指针,来实现objc的动态特性。
所以,isa指针实际上就是ObjC对象和类之间的桥梁,它使ObjC对象能够访问类的信息,并实现ObjC的动态特性。