返回

iOS对象的底层探索(上)

IOS

对于许多开发者来说,每天开发最常敲的代码就是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的动态特性。