【iOS开发】底层探索之对象的底层结构(下)——isa、继承链

探索isa

isa是什么?接上一章节来说,OC对象的本质为结构体NSObject_IMPLNSObject的 结构体,每个OC实例都会包含一个继承自NSObjectisa 指针。但是在__arm64__之前,isa仅仅是一个指针,保存着对象或类对象内存地址,在__arm64__架构之后,apple对isa进行了优化,变成了一个共用体(union)结构,同时使用位域来存储更多的信息。

isa是一个指向Class的指针,那么isa的本质又是什么呢?

struct objc_object {
    private:
        isa_t isa;

    public:
        省略...
}

union isa_t {
    isa_t() { }
    isa_t(uintptr_t value) : bits(value) { }

    Class cls;
    uintptr_t bits;
#if defined(ISA_BITFIELD)
    struct {
        ISA_BITFIELD;  // defined in isa.h
    };
#endif
};

所以isa指针也可以称为isa_t结构体:
其中 ISA_BITFIELD是一个宏定义,在arm64的环境下具体内容如下:

union isa_t 
{
    Class cls;
    uintptr_t bits;
    struct {
         uintptr_t nonpointer        : 1;//->表示使用优化的isa指针
         uintptr_t has_assoc         : 1;//->是否包含关联对象
         uintptr_t has_cxx_dtor      : 1;//->是否设置了析构函数,如果没有,释放对象更快
         uintptr_t shiftcls          : 33; // MACH_VM_MAX_ADDRESS 0x1000000000 ->类的指针
         uintptr_t magic             : 6;//->固定值,用于判断是否完成初始化
         uintptr_t weakly_referenced : 1;//->对象是否被弱引用
         uintptr_t deallocating      : 1;//->对象是否正在销毁
         uintptr_t has_sidetable_rc  : 1;//1->在extra_rc存储引用计数将要溢出的时候,借助Sidetable(散列表)存储引用计数,has_sidetable_rc设置成1
        uintptr_t extra_rc          : 19;  //->存储引用计数,实际的引用计数减一,存储的是其对象以外的引用计数
    };
};

可以看到在 isa_t 联合体中不仅仅表明了指向对象的地址信息,而且这个 64 位数据还记录了其 bits 情况以及该实例每一位保存的对象信息。

在看一张图帮我们理解一下上面的代码:
在这里插入图片描述
我们再来看一下bits的64位存储分布图(以arm64为例)
在这里插入图片描述

从代码中我们可以看见isa_t提供了两个成员变量,一个cls一个bits,cls和bits是互斥的,既只能同时给一个变量赋值。

isa关联类

上一篇博客我们讲解了alloc方法的执行流程,我们来回顾一下基本的流程:alloc ->_class_createInstanceFromZone -> initInstanceIsa ->initIsa,现在我们探究initIsa方法,isa是怎么和类关联起来的,首先先看一下initIsa方法的代码:

inline void 
objc_object::initIsa(Class cls, bool nonpointer, UNUSED_WITHOUT_INDEXED_ISA_AND_DTOR_BIT bool hasCxxDtor)
{ 
    ASSERT(!isTaggedPointer()); 
    
    isa_t newisa(0);

    if (!nonpointer) {
        newisa.setClass(cls, this);
    } else {
        ASSERT(!DisableNonpointerIsa);
        ASSERT(!cls->instancesRequireRawIsa());


#if SUPPORT_INDEXED_ISA
        ASSERT(cls->classArrayIndex() > 0);
        newisa.bits = ISA_INDEX_MAGIC_VALUE;
        // isa.magic is part of ISA_MAGIC_VALUE
        // isa.nonpointer is part of ISA_MAGIC_VALUE
        newisa.has_cxx_dtor = hasCxxDtor;
        newisa.indexcls = (uintptr_t)cls->classArrayIndex();
#else
        newisa.bits = ISA_MAGIC_VALUE;
        // isa.magic is part of ISA_MAGIC_VALUE
        // isa.nonpointer is part of ISA_MAGIC_VALUE
#   if ISA_HAS_CXX_DTOR_BIT
        newisa.has_cxx_dtor = hasCxxDtor;
#   endif
        newisa.setClass(cls, this);
#endif
        newisa.extra_rc = 1;
    }

    // This write must be performed in a single store in some cases
    // (for example when realizing a class because other threads
    // may simultaneously try to use the class).
    // fixme use atomics here to guarantee single-store and to
    // guarantee memory order w.r.t. the class index table
    // ...but not too atomic because we don't want to hurt instantiation
    isa = newisa;
}

在这里插入图片描述
根据调试结果可以看到此时cls的值就是类,并且shiftclass中保存了类的信息,这个过程进行完之后isa就和我们的类关联起来了。

探究Class

我们在学习Class要知道下面这些知识,先放一段代码:

//instance实例对象
        NSObject *object1 = [[NSObject alloc] init];
        NSObject *object2 = [[NSObject alloc] init];

        //class对象,类对象
        //objectClass1~5都是NSObject的类对象
        Class objectClass1 = [object1 class];
        Class objectClass2 = [object2 class];
        Class objectClass3 = [NSObject class];
        Class objectClass4 = object_getClass(object1);
        Class objectClass5 = object_getClass(object2);
        
        //元类对象(将类对象当作参数传入进去)
        Class objectMetaClass = object_getClass([NSObject class]);
        Class objectMetaClass2 = [[NSObject class] class];
        
        //判断是不是元类对象

        NSLog(@"instance - %p %p", object1, object2);
        NSLog(@"class - %p %p %p %p %p %d", objectClass1,objectClass2, objectClass3, objectClass4, objectClass5, class_isMetaClass(objectClass3 ));
        NSLog(@"mateClass - %p %p %d",objectMetaClass, objectMetaClass2, class_isMetaClass(objectMetaClass));

输出情况:
instance - 0x100511920 0x10050e840
class - 0x7fff91da2118 0x7fff91da2118 0x7fff91da2118 0x7fff91da2118 0x7fff91da2118 0
mateClass - 0x7fff91da20f0 0x7fff91da2118 1

首先我们先了解一下iOS-实例对象( instance)、类对象(class)、元类对象(meta-class)的内部结构分析

Class是什么?Class本质上是一个结构体类型:

typedef struct objc_class *Class;

实例对象(instance)

实例对象(instance)是什么?实例对象的内部结构是什么?

首先instance对象是通过类alloc init出来的对象,每次alloc init都会产生新的instance对象。

其次实例对象的内部结构为:

struct objc_object {
private:
    isa_t isa;    
    //以下省略是其他继承添加成员变量
}

实例对象(instance)在内存中存储的信息有isa,成员变量的具体值,所以实例对象在内存中分配有不同的内存和存储空间

class对象 (类对象)

通过以下方法可以获得类对象,每个类在内存中有且只有一个class对象。

   Class personClass1 = [person1 class];
   Class personClass2 = [Person class];
   Class personCalss3 = object_getClass(person1);
   NSLog(@"---personClass1:%p",personClass1);
   NSLog(@"---personCalss2:%p",personClass2);
   NSLog(@"---personCalss3:%p",personCalss3);

class对象 (类对象)里面存放有isa,superclass,属性,对象方法,协议和成员变量(注意:实例对象中存放的是成员变量的具体值,在这里存放的是成员变量信息,比如名称类型等)。

meta-class对象(元类对象)

怎么创建元类对象呢,如下所示:

   Person *person1 = [[Person alloc]init];
   Class personClass1 = [person1 class];
  // 元类对象
   Class metaClass = object_getClass(personClass1);
   NSLog(@"---personClass1:%p",personClass1);
   NSLog(@"---metaClass:%p",metaClass);

与类对象一样,每个类在内存中只要一个meta-calss类。里面存放的有isa,superclass,类对象

关于Class的一些问题以及总结

		//创建实例对象
       	NSObject *obj1 = [[NSObject alloc] init];
        NSObject *obj2 = [[NSObject alloc] init];
        
        NSLog(@"instance obj: %p, %p", obj1, obj2);
        //创建Class类对象
        Class ClassObj1 = [NSObject class];
        Class ClassObj2 = [obj1 class];
        Class ClassObj3 = [obj2 class];
        /
        Class ClassObj4 = objc_getClass("NSObject");
        Class ClassObj5 = object_getClass(obj1);
        NSLog(@"class obj: %p, %p, %p, %p, %p",
              ClassObj1,
              ClassObj2,
              ClassObj3,
              ClassObj4,
              ClassObj5);
        //创建metaClass元类对象
        Class metaClass1 = object_getClass([NSObject class]);
        NSLog(@"metaClass obj: %p",
        metaClass1);

看上边的代码,从内存的角度来看

  • 实例对象在内存中的地址不相同,可以得出每次实例经过 alloc,都会有自己的独占空间,是一个全新的对象
  • 类对象无论用哪一种方式获得,从 obj1 或者 obj2 等等其他方式,最终拿到的内存地址都是相同,说明他们共享了一个类对象
  • 元类对象类对象获得,说明也和实例无关,也是一个共享对象

类对象和元类对象都是Class类型的,那么它们之间有什么联系呢?

  • NSObject元类对象类对象在内存中都只有一份
  • 类对象元类对象类型相同,都是Class类型
  • Class 是一种结构体指针,说明 类对象元类对象 其实拥有相同的结构

换取方式来看,可以得到以下知识点:

  • -(Class)class + (Class)class 这两种方式获得的都是 类对象
  • objc_getClass 获取的也是 类对象
  • Class object_getClass(id _Nullable obj) 比较复杂,传入 instance对象 返回 class对象;传入 class对象 返回 meta-class对象; 传入 class对象 返回 NSObject(基类)的 meta-class 对象

很重要的一张图!
在这里插入图片描述

  • instance对象的isa指向class对象
  • class对象的isa指向meta-class对象
  • mate-class对象的isa指向基类的mate-class对象
  • class的superclass指向父类的class
  • 如果没有父类,superclass指针为nil
  • meta-class的superclass指向父类的meta-class
  • 基类的meta-class的superclass指向基类的class

struct objc_class的结构

先看一下它的代码:

struct objc_class {
   // 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的理解
rw代表可读可写
类中的属性、方法、协议等信息都保存在class_rw_t中

对class_ro_t的理解
ro代表只读
存储了当前类在编译期就确定了的属性、方法、协议

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值