类结构探究(一)-- isa与superclass的指向

通过之前对对象创建流程的探究(详情见OC对象alloc流程分析),我们知道最终会走到_class_createInstanceFromZone方法,其中开辟空间后,程序返回的是一个id类型的变量,关于id类型我们都知道它可以表示任意类型的对象,那究竟id类型在底层是如何实现的呢?我们先在objc4源码中一探究竟:

万物皆对象之objc_object

objc-private.h第60行,id是这么定义的:

typedef struct objc_object *id;

可以这么下定义:id是指向objc_object结构体的指针。

由此也可以推论:绝大多数的OC对象底层实现都是objc_object结构体

继承自NSProxy的对象不在辐射范围内。本文所提对象均是NSObject对象

查看objc_object

struct objc_object {
private:
    isa_t isa;

public:

    // ISA() assumes this is NOT a tagged pointer object
    Class ISA();

    // rawISA() assumes this is NOT a tagged pointer object or a non pointer ISA
    Class rawISA();

    // getIsa() allows this to be a tagged pointer object
    Class getIsa();
    
    // 后面都是方法实在太多
    ...
};

objc_object结构体中只有一个成员isa,这也是为什么对象都有一个isa属性。

关于isa_t,在isa结构解析中已经做了探究。

关于类的探究--objc_class

同样在objc-private.h,在第59行Class是这么定义的:

typedef struct objc_class *Class;

查看objc_class的定义,在objc-runtime-new.h第1244行至1629是这么定义(runtime.h中关于objc_class的定义苹果已弃用)。

struct objc_class : objc_object {
    // Class ISA;
    Class superclass;
    cache_t cache;             // formerly cache pointer and vtable
    class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags

    class_rw_t *data() const {
        return bits.data();
    }
    void setData(class_rw_t *newData) {
        bits.setData(newData);
    }
    
    // 后面都是方法
    ...

};

可以看到objc_class继承自objc_object,所以除了自己的superclasscachebits外,还有来自父类的isa

实例对象、类、元类的isa

我们知道实例对象的isa指向类对像,那么类对象的isa指向哪里呢,我们可以通过lldb去探究它的指向关系。

首先创建两个类AnimalCat,其中Animal继承自NSObject,Cat继承自Animal.

@interface Animal : NSObject

@end

@interface Cat : Animal

@property(nonatomic, copy) NSString *name;

@end

创建Cat实例对像,并打上断点。

Cat *cat = [[Cat alloc] init];
cat.name = @"mimi";// 此处打上断点

运行程序,来到断点后,通过lldb命令x/4gx cat打印cat内存信息得到:

0x10062ad80: 0x001d80010000222d 0x0000000000000000
0x10062ad90: 0x63756f54534e5b2d 0x7473754372614268

我们知道首个位置存储的是isa,而对于非纯指针类型的isa,它只有中间33位或44位表示类信息.

我们可以通过位lldb验证实例对象的isa指向类对象。

(lldb) p/x  0x001d80010000222d & 0x00007ffffffffff8ULL
(unsigned long long) $8 = 0x0000000100002228

(lldb) p/x cat.class
(Class) $9 = 0x0000000100002228 Cat

继续通过lldb查看类型的内存信息

(lldb) x/4gx 0x0000000100002228
0x100002228: 0x0000000100002200 0x0000000100002278
0x100002238: 0x0000000100415790 0x0002801c00000007

首个位置依旧是isa,打印查看他的指向

(lldb) po 0x0000000100002200 & 0x00007ffffffffff8ULL 
Cat
(lldb) p/x 0x0000000100002200 & 0x00007ffffffffff8ULL 
(unsigned long long) $11 = 0x0000000100002200

神奇的是,类的isa也指向了一个Cat类,并且这两个类的内存地址不同,难道有两个类?实际上,内存中只有唯一个Cat类对象,类对象isa所指向类成为元类,它是由编译器创建和管理的。

我们可以继续查看Cat元类的内存信息

(lldb) x/4gx 0x0000000100002200
0x100002200: 0x00007fff8fe210f0 0x0000000100002250
0x100002210: 0x0000000100704110 0x0003e03500000007

元类也有isa属性,继续查看它的指向

(lldb) po 0x00007fff8fe210f0  & 0x00007ffffffffff8ULL
NSObject

(lldb) p/x 0x00007fff8fe210f0  & 0x00007ffffffffff8ULL
(unsigned long long) $13 = 0x00007fff8fe210f0

(lldb) p/x NSObject.class
(Class) $14 = 0x00007fff8fe21118 NSObject

元类的isa指向了NSObject,并且通过打印信息我们知道不是NSObject类,而是NSObject元类,即根元类,那么根元类的isa指向哪里呢?接着探究

(lldb) x/4gx $13
0x7fff8fe210f0: 0x00007fff8fe210f0 0x00007fff8fe21118
0x7fff8fe21100: 0x0000000100724040 0x0005e03100000007

可以看到NSObject元类的isa(0x00007fff8fe210f0)和它自己的地址(0x00007fff8fe210f0)完全一致,所以可以得出结论,根元类的isa指向根元类本身

至此,可以得出结论:
实例对象的isa指向类对象,类对象的isa指向元类,元类的isa指向根元类,根元类的isa指向它自己

爸爸去哪里之superclass

除了isa,objc_class还有一个重要属性superclass,故名思义这是一个决定继承关系的属性,我们可以继续通过lldb探究。

继续前面的Cat类信息打印

(lldb) x/4gx 0x0000000100002228
0x100002228: 0x0000000100002200 0x0000000100002278
0x100002238: 0x0000000100415790 0x0002801c00000007

(lldb) po 0x0000000100002278
Animal

不出意料,superclass存放了父类Animal的类信息。

那么Animalsuperclass也必定指向NSObject

(lldb) x/4gx 0x0000000100002278
0x100002278: 0x0000000100002250 0x00007fff8fe21118
0x100002288: 0x00007fff6868b140 0x0000801000000000

(lldb) po 0x00007fff8fe21118
NSObject

那么根类NSObject的superclass指向哪里呢?

(lldb) x/4gx 0x00007fff8fe21118
0x7fff8fe21118: 0x00007fff8fe210f0 0x0000000000000000
0x7fff8fe21128: 0x0000000100636a40 0x0002801000000003

答案是nil

除了类的父类指向,元类的superclass是怎么指向的呢,继续探究

(lldb) x/4gx 0x0000000100002200
0x100002200: 0x00007fff8fe210f0 0x0000000100002250
0x100002210: 0x0000000100704110 0x0003e03500000007

(lldb) po 0x0000000100002250
Animal

这里Animal是元类的Animal(可以通过地址去判断),所以,Animalsuperclass也指向根元类NSObject

(lldb) x/4gx 0x0000000100002250
0x100002250: 0x00007fff8fe210f0 0x00007fff8fe210f0
0x100002260: 0x0000000100636da0 0x0001e03100000007

(lldb) po 0x00007fff8fe210f0
NSObject

根元类NSObjectsuperclass是否也指向nil呢?继续探索。

(lldb) x/4gx 0x00007fff8fe210f0
0x7fff8fe210f0: 0x00007fff8fe210f0 0x00007fff8fe21118
0x7fff8fe21100: 0x0000000100724040 0x0005e03100000007

(lldb) po 0x00007fff8fe21118
NSObject

(lldb) p/x NSObject.class
(Class) $24 = 0x00007fff8fe21118 NSObject

出乎意料,根元类的superclass不是指向nil,而是指向根类NSObject

总结

最后,我们可以得出以下结论

  1. 实例对象的isa指向类对象
  2. 类对象的isa指向元类
  3. 元类的isa指向根元类
  4. 根元类的isa指向自身
  5. 实例对像没有superclass
  6. 类对象的superclass指向父类对象,最终指向根类对象
  7. 根类对象的superclass指向nil
  8. 元类的superclass指向父元类,最终指向根元类
  9. 根元类的superclass指向根类

可以用下面这张经典的图表示他们之间的“羁绊”

isa流程图.png

你可能感兴趣的:(类结构探究(一)-- isa与superclass的指向)