OC底层探索(四) ISA的结构与类的关联、ISA走位分析

联合体

概念

联合体又称共用体,联合体是由多种类型的变量组成,但多个变量共用一块内存,所以变量之间是互斥的,联合体中的变量只能存在一个有值,如果有别的变量被赋值,那么之前的变量就会被覆盖掉。

案例

 union {
        
        char bits;
        // 增加代码的可读性,相对于这个联合体就是说这一个字节的各个位都是做什么用的,占用几位,总共是用一个字节而已
        struct {
            // 位域名 : 位域长
            char left: 1;
            char right: 1;
            char top: 1; 
            char bottom: 1
        } my;
        
    } myUnion;

总结

  1. 联合体中可以定义多个成员,联合体的大小由最大的成员大小决定
  2. 联合体的成员公用一个内存,一次只能使用一个成员
  3. 对某一个成员赋值,会覆盖其他成员的值
  4. 节省了大量的存储空间, 存储效率更高,可读性更强,可以提高代码的可读性,可以使用位运算提高数据的存储效率。

isa

isa是什么

isa是一个Class 类型的指针,每个实例对象的第一个成员变量就是isa指针,他指向对象的类,而Class里也有个isa的指针, 指向meteClass(元类)。

OC底层探索(一):alloc、init、new源码分析文章中我们介绍到alloc在开辟完内存后,使用iSA进行与类关联,那这个过程是什么样的呢?今天我们探索一下initInstanceIsa是如何执行的。

isa的结构

inline void 
objc_object::initInstanceIsa(Class cls, bool hasCxxDtor)
{
    ASSERT(!cls->instancesRequireRawIsa());
    ASSERT(hasCxxDtor == cls->hasCxxDtor());
    //初始化isa
    initIsa(cls, true, hasCxxDtor); 
}

然后点进initIsa 方法:

inline void 
objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor) 
{ 
    assert(!isTaggedPointer()); 
    
    if (!nonpointer) {
        isa.cls = cls;
    } else {
        assert(!DisableNonpointerIsa);
        assert(!cls->instancesRequireRawIsa());
//初始化一个isa_t
        isa_t newisa(0);

#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
        newisa.has_cxx_dtor = hasCxxDtor;
        newisa.shiftcls = (uintptr_t)cls >> 3;
#endif

        // 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;
    }
}

在代码中我们可以发现isa_t newisa(0); 申请了一个isa_t,那isa_t是什么呢?

isa指针的类型是由isa_t定义,从定义中可以看出是通过联合体(union)定义的.

union isa_t { //联合体
    isa_t() { }
    isa_t(uintptr_t value) : bits(value) { }
    //提供了cls 和 bits ,两者是互斥关系
    Class cls;
    uintptr_t bits;
#if defined(ISA_BITFIELD)//位域
    struct {
        ISA_BITFIELD;  // defined in isa.h
    };
#endif
};

在源码中我们可以发现isa的联合体中有两个变量,分别是cls和bits

  • cls:是Class类型的指针变量,指向的是对象的类。
  • bits:是结构体位域指针。
  • ISA_BITFIELD:宏 ISA_BITFIELD,用来定义位域,用于存储类信息及其他信息。
ISA_BITFIELD

ISA_BITFIELD 宏在内部分别定义了arm64位架构(iOS)和x86_64架构(macOS)的掩码和位域.。在这里插入图片描述
(注:图片来自“style_月月”的博客)

我们以__arm64__为例,分别解析一下ISA_BITFIELD的属性:
在这里插入图片描述

isa与类的关联

关联

以下是initIsa的源码:

inline void 
objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor) 
{ 
    assert(!isTaggedPointer()); 
    
    if (!nonpointer) {
        isa.cls = cls;//使用cls定义isa
    } else {
        assert(!DisableNonpointerIsa);
        assert(!cls->instancesRequireRawIsa());
//初始化一个isa_t
        isa_t newisa(0);//初始化isa_t 

#if SUPPORT_INDEXED_ISA //使用cls定义
        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//nonpointer
        newisa.bits = ISA_MAGIC_VALUE;
        // isa.magic is part of ISA_MAGIC_VALUE
        // isa.nonpointer is part of ISA_MAGIC_VALUE
        newisa.has_cxx_dtor = hasCxxDtor;
        newisa.shiftcls = (uintptr_t)cls >> 3;
#endif

        // 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;
    }
}

在源码中我们可以看到initIsa主要做了两个事:

  • 通过cls初始化isa
    如果是非nonpointer,代表普通的指针,存储着Class、Meta-Class对象的内存地址信息
  • 通过bits初始化isa
    如果是nonpointer,则会进行一系列的初始化操作.其中的newisa.shiftcls = (uintptr_t)cls >> 3;中的shiftcls存储着Class、Meta-Class对象的内存地址信息

验证

1.通过 isa & ISA_MSAK
  • ISA_MSAK: arm64中 值为 0x0000000ffffffff8ULL;
    x86_64中 值为 0x00007ffffffffff8ULL

  • _class_createInstanceFromZone方法,此时cls isa已经关联完成

  • 执行po objc查看是否绑定成功

  • 执行x/4gx obj,得到isa指针的地址0x001d8001000020e9

  • 将isa指针地址 & ISA_MASK (处于macOS,使用x86_64中的宏定义),即 po 0x001d8001000020e9 & 0x00007ffffffffff8 ,得出LGPerson
    在这里插入图片描述

2.通过位运算
  • _class_createInstanceFromZone方法,此时cls isa已经关联完成
  • 执行x/4gx obj,得到isa指针的地址0x001d8001000020e9,在isa指针中shiftcls存储的是类信息,且shiftcls此时占44位(macOS)。

在这里插入图片描述

  • 想要读取中间的44类信息,就需要经过位运算 ,将右边3位,和左边除去44位以外的部分都抹,其相对位置是不变的。

位运算步骤:

  • isa地址右移3位:p/x 0x001d8001000020e9 >> 3 ,得到0x0003b0002000041d
  • 在将得到的0x0003b0002000041d左移20位:p/x 0x0003b0002000041d << 20,得到0x0002000041d00000
  • 将得到的0x0002000041d00000 再右移17位:p/x 0x0002000041d00000 >> 17得到新的0x00000001000020e8
  • 获取cls的地址 与 上面的进行验证 :p/x cls 也得出0x00000001000020e8,所以由此可以证明 cls isa是关联的

在这里插入图片描述

isa指向走位

(注:虚线代表isa;实线代表继承)
在这里插入图片描述
解析:

  • Isa:对象的isa指向i类对象类对象的isa指向i元类元类的isa指向i根元类(NSObject的元类) 根元类(NSObject的元类) 的isa指向i自己
  • 继承:类对象的继承指向 父类对象父类对象的继承指向 NSObjectNSObject的继承指向nil;
  • 元类的继承指向 父类元类父类元类的继承指向 根元类(NSObject的元类)根元类(NSObject的元类)的继承指向nil;

元类

有上述我们知道了isa继承的走向,类对象的isa指向了元类,那元类是个什么东西呢?

  • 在OC中其实也是一个对象,称之为类对象,其isa的位域执行苹果定义的元类
  • 元类是系统生成的,其定义创建都是由编译器完成的,在这个过程中,归属来自于元类
  • 元类类对象,每个都有一个独一无二的元类,用来存储类方法等相关信息

**问题 **:
Student继承于Person,那么Student类型的对象student与Person类型的对象person是什么关系?
答案是:没有任何关系!
类是可以继承的,但是对象之间是没有任何关系的。

验证

准备工作

  • 创建类Person,继承于NSObject:
    .h文件
#import <Foundation/Foundation.h>

@interface Person : NSObject
    @property (nonatomic, copy) NSString *name;
    @property (nonatomic, copy) NSString *nickName;
    // @property (nonatomic, copy) NSString *hobby;
    @property (nonatomic, assign) int age;
    @property (nonatomic, assign) long height;
    
    @property (nonatomic) char c1;
    @property (nonatomic) char c2;
    
@end
  • 创建Student类,继承于Person
    .h文件
#import <Foundation/Foundation.h>
#import "Person.h"

@interface Student : Person

@end
  • 在main.m文件中初始化Person和Student
 #import <UIKit/UIKit.h>
#import "AppDelegate.h"
#import "Person.h"
#import "Student.h"

int main(int argc, char * argv[]) {
    @autoreleasepool {
        //0x00007ffffffffff8ULL
        Person *person = [Person alloc];
        Student *student = [Student alloc];
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
        }

验证person 的isa走位
通过lldb命令打印探索isa的走位:
在这里插入图片描述
验证student的isa走位

在这里插入图片描述
验证类对象的继承
在这里插入图片描述
验证元类的继承
在这里插入图片描述

总结

  • isa走向:对象 -> 类对象 -> 元类 -> 根元类(NSObject的元类) -> 自己(根元类)
  • 继承:类对象 -> 父类对象 -> NSObject -> nil
    元类 –> 父类元类 ->根元类(NSObject的元类) -> NSObject -> nil;
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值