iOS开发技巧

OC底层原理04—类、元类、根元类 与 isa的关联

2020-09-15  本文已影响0人  夏天的枫_

如题,今天来探究下类的一些信息,isa指针的指向就是该类对象的元类,每一个类都是它的元类的对象,元类是对类对象的描述。首先来分析一下代码

//类对象内存存在个数为 1
void lgTestClassNum(){
    Class class1 = [LGPerson class];
    Class class2 = [LGPerson alloc].class;
    Class class3 = object_getClass([LGPerson alloc]);
    Class class4 = [LGPerson alloc].class;
    NSLog(@"\n%p-\n%p-\n%p-\n%p",class1,class2,class3,class4);
}

void lgTestNSObject(){
    // NSObject实例对象
    NSObject *object1 = [NSObject alloc];
    // NSObject类
    Class class = object_getClass(object1);
    // NSObject元类
    Class metaClass = object_getClass(class);
    // NSObject根元类
    Class rootMetaClass = object_getClass(metaClass);
    // NSObject根根元类
    Class rootRootMetaClass = object_getClass(rootMetaClass);
    NSLog(@"\n%p 实例对象\n%p 类\n%p 元类\n%p 根元类\n%p 根根元类",object1,class,metaClass,rootMetaClass,rootRootMetaClass);
}

打印结果分析之:

2020-09-14 23:24-isa分析[1763:92018] <LGPerson: 0x102055210>
2020-09-14 23:24-isa分析[1763:92018] 
0x100002588-
0x100002588-
0x100002588-
0x100002588
2020-09-14 23:24-isa分析[1763:92018] 
0x1005135b0 实例对象
0x7fffb1bd0140 类
0x7fffb1bd00f0 元类
0x7fffb1bd00f0 根元类
0x7fffb1bd00f0 根根元类

可以看到lgTestClassNum中打印的类对象地址都是同一个,说明:类对象存在个数仅有一份。在lgTestNSObject中实例对象地址与类的地址,以及元类、根元类、根根元类的地址是不一样的,其中元类、根元类、根根元类的地址则是一样的。这是为什么呢?
在系列文章前几章中我们了解了isa的结构,类的首地址就是isa,isa中的shiftcls存储着类的一些信息,isa在类中存在这非常重要的关系,继续通过LLDB分析之。

isa在类中用lldb分析
isa指向:对象->类对象->元类对象->根元类对象,根元类对象的isa指向根元类自身
元类可以存储类的自有信息,可供子类继承的类信息。
实例对象没有继承关系,只有类与类间才有继承关系;
根元类的父类为NSObject,NSObject 继承的地址没有,所以父类为nil。
经典isa走位图
实例对象的isa指向它的类对象Subclass,Subclass继承自它的父类Superclass,而Subclass的isa指向它的元类Subclass(meta),类和元类的地址是一样的。

拓展:objc_class vs objc_object 关系?

objc_object 和实例对象的关系?

所有的对象都是继承NSObject,NSObject又继承自底层的objc_object(C/C++)结构体类型,底层的是没有对象而是结构体。

类的内存 + 对象的内存

class_data_bits_t
先拓展:内存偏移

再来分析下类objc_object的结构信息

// 注意new 、old版本差,此版本为objc4-781
struct objc_class : objc_object {
    // Class ISA;  // 默认8字节
    Class superclass;  // 8字节
    cache_t cache;  // 8 + 4 + 2 + 2      // formerly cache pointer and vtable
    class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags

    /*篇幅有限,省略N多代码,主要代码分析上面即可*/
};

class_data_bits_t存储了类中的属性(property)、方法(method)、协议(protocol),这是类结构信息中的关键部分,通过之前的LLDB我们是可以获得类对象的首地址的,通过内存偏移的方式获取class_data_bits_t
思路是:isa 8字节 + superClass 8字节 + cache_t(mask_t : 4 + buckerts : 8 + xxx :2 + xxx:2 = 16)。
isa属性:继承自objc_object的isa,占 8字节
superclass 属性:Class类型,Class是由objc_object定义的,是一个指针,占8字节
cache属性:简单从类型class_data_bits_t目前无法得知,而class_data_bits_t是一个结构体类型,结构体的内存大小需要根据内部的属性来确定,而结构体指针才是8字节
bits属性:只有首地址经过上面3个属性的内存大小总和的平移,才能获取到bits

通过LLDB分析:


获取ro_or_rw信息 打印propertylist

781没有了propertylis, methodlist, protocllist. p $x 打印的是类型(刀x:是地址的别名)

上一篇下一篇

猜你喜欢

热点阅读