OC底层原理06 - 类结构探索(1)

2020-09-25  本文已影响0人  卡布奇诺_95d2

类是什么?

类:指向objc_class结构体的指针

typedef struct objc_class *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
}

对象:指向objc_object结构体的指针

typedef struct objc_object *id;

struct objc_object {
private:
    isa_t isa;
}

从上面源码中可以得到,objc_class继承于objc_object。换句话说,objc_class也是一个对象,后面称为类对象

objc_class具有以下几个特点:

总结:也是一种对象,在里面定义了一些属性和方法

“万物皆对象”可能是由此而来的。

isa指针

objc_class结构体中,第一个成员就是isa,在之前的文章中,已经描述了如何将对象的isa与类关联起来,可是为什么在类的结构体里面又有isa呢?这个isa又指向什么呢?
说到isa指针,不得不拿出isa走位图,该图清晰的描述了isa的指向

isa走位图.png

元类

在分析isa走位图之前,先引入一个概念元类

  1. 元类系统给的,其定义和创建都是由编译器完成。
  2. 元类类对象,每一个类都有一个独一无二的元类存储类方法的相关信息。
  3. 元类本身是没有名称的,由于与类有关联,所以同类名一样的名称。
  4. 继承链中处于顶端元类,称为根元类。即所有NSObject子类元类都会以NSObject的元类为它们的根元类

流程图分析

从图中,可以得到以下信息:

1. 对象的isa指向类;
2. 类的isa指向元类;
3. 元类的isa指向根元类;
4. 根元类的isa指向自己;

验证

准备工作:

  1. 定义一个LGPerson类,使其继承于NSObject;
  2. 定义一个LGStudent类,使其继承于LGPerson;
  3. 最后在main函数中定义LGPerson对象和LGStudent对象。
//定义一个LGPerson类,继承于NSObject
@interface LGPerson : NSObject

@property(nonatomic, strong) NSString* name;

- (void)sayInstanceMethod;

+ (void)sayClassMethod;

@end

@implementation LGPerson

- (void)sayInstanceMethod{
    NSLog(@"%s", __func__);
}

+ (void)sayClassMethod{
    NSLog(@"%s", __func__);
}

@end

//定义一个LGStudent类,继承于LGPerson
@interface LGStudent : LGPerson

@end

@implementation LGStudent

@end

//在main.cpp中定义两个对象
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        LGPerson *person = [LGPerson alloc];
        LGStudent* student = [LGStudent alloc];
        NSLog(@"Hello, World!  %@, %@",person, student);
    }
    return 0;
}

在NSLog处打断点,让程序暂停在此处。接下来开始使用lldb进行调试。

(lldb) p/x person
(LGPerson *) $0 = 0x00000001006ebc90
(lldb) x/4gx $0
0x1006ebc90: 0x001d800100008255 0x0000000000000000
0x1006ebca0: 0x697263534b575b2d 0x67617373654d7470
(lldb) po 0x001d800100008255 & 0x00007ffffffffff8ULL
LGPerson

(lldb) p 0x001d800100008255 & 0x00007ffffffffff8ULL
(unsigned long long) $2 = 0x0000000100008250

此时得到的LGPerson是类还是元类呢?通过runtime接口打印一下即可。

//获取类的地址
(lldb) p/x person.class
(Class) $12 = 0x0000000100008250 LGPerson

//获取元类的地址
(lldb) p/x objc_getMetaClass("LGPerson")
(Class) $13 = 0x0000000100008228

此时可以知道。0x0000000100008250就是person对象的类。

得到重要线索1:对象的isa指向类,此处person对象的isa指向LGPerson类

既然说类也是一个对象,那我们就读取一下类首地址处的内存,看看有什么收获?

(lldb) x/4gx 0x0000000100008250
0x100008250: 0x0000000100008228 0x000000010034c140
0x100008260: 0x0000000100346430 0x0000801c00000000

根据类的定义,第一个元素仍然是isa,那它会指向哪里?

(lldb) po 0x0000000100008228 & 0x00007ffffffffff8ULL
LGPerson

(lldb) p/x 0x0000000100008228 & 0x00007ffffffffff8ULL
(unsigned long long) $3 = 0x0000000100008228

得到重要线索2:类的isa指向元类,此处LGPerson类的isa指向LGPerson元类。

//读取该地址(LGPerson的isa指向的类首地址)下在的内存
(lldb) x/4gx 0x0000000100008228
0x100008228: 0x000000010034c0f0 0x000000010034c0f0
0x100008238: 0x0000000103a040a0 0x0003e03500000007

//获取内存中isa指向的类信息
(lldb) po 0x000000010034c0f0 & 0x00007ffffffffff8ULL
NSObject

//继续获取isa指向的内存地址
(lldb) p 0x000000010034c0f0 & 0x00007ffffffffff8ULL
(unsigned long long) $4 = 0x000000010034c0f0

//读取isa指向的内存地址
(lldb) x/4gx 0x000000010034c0f0
0x10034c0f0: 0x000000010034c0f0 0x000000010034c140
0x10034c100: 0x0000000103a044b0 0x0004e03100000007

//获取内存中isa指向的类信息
(lldb) po 0x000000010034c0f0 & 0x00007ffffffffff8ULL
NSObject

此时类信息已经变成NSObject,并且继续对NSObject读取isa指向时,发现一直指向的同一个NSObject(因为地址没有发生改变)。

得到两条线索:
1. LGPerson元类isa指向根元类,此处的根元类NSObject;
2. 根元类isa指向根元类自己

通过一步一步读取isa的指向,验证了isa走位图isa的走位

总结:

作为重点,这里再强调一遍isa的走位。
1. 实例对象(Instance of Subclass)的isa指向类(class)
2. 类对象(class)的isa指向元类(Meta class)
3. 元类(Meta class)的isa指向根元类(Root metal class)
4. 根元类(Root metal class)的isa指向它自己本身

supperclass

isa走位图里面还描述了一个superclass,表示继承关系,图中描述的是:

  1. 子类的类对象superclass指向的是它的父类的类对象,即子类继承父类
  2. 子类的元类对象superclass指向的是它的父类的元类对象,即子元类继承父元类
  3. 根元类对象superclass指向根类对象,即根元类继承根类
  4. 最终的superclass指向nil

接下来也来验证一下:

//NSObject 类地址
(lldb) p/x objc_getClass("NSObject")
(Class) $1 = 0x000000010034c140 NSObject

//NSObject 元类地址
(lldb) p/x objc_getMetaClass("NSObject")
(Class) $2 = 0x000000010034c0f0

//LGPerson 类地址
(lldb) p/x objc_getClass("LGPerson")
(Class) $3 = 0x00000001000082e8 LGPerson

//LGPerson 元类地址
(lldb) p/x objc_getMetaClass("LGPerson")
(Class) $4 = 0x00000001000082c0

//LGStudent 类地址
(lldb) p/x objc_getClass("LGStudent")
(Class) $5 = 0x0000000100008338 LGStudent

//LGStudent 元类地址
(lldb) p/x objc_getMetaClass("LGStudent")
(Class) $6 = 0x0000000100008310
(lldb) p/x LGStudent.superclass
(Class) $7 = 0x00000001000082e8 LGPerson

得出结论1:LGStudent 类supperclassLGPerson类

(lldb) p/x LGPerson.superclass
(Class) $8 = 0x000000010034c140 NSObject

得出结论2:LGPerson 类supperclassNSObject类

(lldb) p/x NSObject.superclass
(Class) $9 = nil

得出结论3:NSObject 类supperclassnil

(lldb) p/x ((LGStudent*)objc_getMetaClass("LGStudent")).superclass
(Class) $10 = 0x00000001000082c0

得出结论4:LGStudent 元类supperclassLGPerson 元类

(lldb) p/x ((LGPerson*)objc_getMetaClass("LGPerson")).superclass
(Class) $11 = 0x000000010034c0f0

得出结论5:LGPerson 元类supperclassNSObject 元类

(lldb) p/x ((NSObject*)objc_getMetaClass("NSObject")).superclass
(Class) $12 = 0x000000010034c140 NSObject

得出结论6:NSObject 元类supperclassNSObject 类
至此,已经完成supperclass走位图的验证。

总结:
1. 类(subClass)继承自父类(superClass)
2. 父类(superClass)继承自根类(RootClass)
3. 根类继承自nil根类可以理解为万物起源
4. 子类的元类(metal SubClass)继承自父类的元类(metal SuperClass)
5. 父类的元类(metal SuperClass)继承自根元类(Root metal Class)
6. 根元类(Root metal Class)继承自根类(Root class)

上一篇下一篇

猜你喜欢

热点阅读