美文网首页
isa构造那点事(三)

isa构造那点事(三)

作者: 会跑的鱼_09 | 来源:发表于2020-09-10 22:56 被阅读0次

背景

在上篇对象alloc那点事中分析对象创建过程,最终是走到_class_createInstanceFromZone方法,这个方法中有三个关键步骤:

//确认创建类需要开辟的内存大小
size = cls->instanceSize(extraBytes);
//开辟内存
obj = (id)calloc(1, size);
//把isa信息填充到上面申请到的内存中
obj->initInstanceIsa(cls, hasCxxDtor);

其中instanceSize在OC对象大小那点事已经分析过了,而关于calloc涉及到比较底层的知识,有兴趣可以参考iOS 高级之美(六)—— malloc分析,今天我们来看一下initInstanceIsa这个方法到底干了啥~

一、initInstanceIsa方法执行流程

objc_object::initInstanceIsa(Class cls, bool hasCxxDtor)
{
    initIsa(cls, true, hasCxxDtor);
}
inline void 
objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor) 
{ 
    ASSERT(!isTaggedPointer()); 
    //是否对isa开启指针优化,所有自定义的oc类都是开启的
    if (!nonpointer) {
        isa = isa_t((uintptr_t)cls);
    } else {
        ASSERT(!DisableNonpointerIsa);
        ASSERT(!cls->instancesRequireRawIsa());

        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
        isa = newisa;
    }
}

查看SUPPORT_INDEXED_ISA的定义:

#if __ARM_ARCH_7K__ >= 2  ||  (__arm64__ && !__LP64__)
#   define SUPPORT_INDEXED_ISA 1
#else
#   define SUPPORT_INDEXED_ISA 0
#endif
  • __ARM_ARCH_7K__: 是否手表系统
  • __arm64__: 是否arm架构
  • __LP64__:是否64位系统
    在mac电脑上进行debug调试,非手表、非arm架构、64位系统,执行到此处SUPPORT_INDEXED_ISA为0。所以关键代码是
//给bits一个初始值
newisa.bits = ISA_MAGIC_VALUE;
//标记是否有析构方法
newisa.has_cxx_dtor = hasCxxDtor;
//把cls信息赋值给shiftcls
newisa.shiftcls = (uintptr_t)cls >> 3;

可以看到关键点是构造newisa这个对象并赋值给isa,newisa的类型是isa_t,下面我们来看看它到底是如何实现的。

二、isa_t结构分析

继续跟踪源码,看一下isa_t的定义:

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_BITFIELD定义如下
# if __arm64__
#   define ISA_MASK        0x0000000ffffffff8ULL
#   define ISA_MAGIC_MASK  0x000003f000000001ULL
#   define ISA_MAGIC_VALUE 0x000001a000000001ULL
#   define ISA_BITFIELD                                                      \
      uintptr_t nonpointer        : 1;                                       \
      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;                                       \
      uintptr_t extra_rc          : 19
#   define RC_ONE   (1ULL<<45)
#   define RC_HALF  (1ULL<<18)

# elif __x86_64__
#   define ISA_MASK        0x00007ffffffffff8ULL
#   define ISA_MAGIC_MASK  0x001f800000000001ULL
#   define ISA_MAGIC_VALUE 0x001d800000000001ULL
#   define ISA_BITFIELD                                                        \
      uintptr_t nonpointer        : 1;                                         \
      uintptr_t has_assoc         : 1;                                         \
      uintptr_t has_cxx_dtor      : 1;                                         \
      uintptr_t shiftcls          : 44; /*MACH_VM_MAX_ADDRESS 0x7fffffe00000*/ \
      uintptr_t magic             : 6;                                         \
      uintptr_t weakly_referenced : 1;                                         \
      uintptr_t deallocating      : 1;                                         \
      uintptr_t has_sidetable_rc  : 1;                                         \
      uintptr_t extra_rc          : 8
#   define RC_ONE   (1ULL<<56)
#   define RC_HALF  (1ULL<<7)

各成员字段说明如下:

成员 说明
nonpointer 表示是否对 isa 指针开启指针优化 0:纯isa指针,1:不止是类对象地址,isa 中包含了类信息、对象的引用计数等
has_assoc 关联对象标志位,0没有,1存在
has_cxx_dtor 该对象是否有 C++ 或者 Objc 的析构器,如果有析构函数,则需要做析构逻辑, 如果没有,则可以更快的释放对象
shiftcls 存储类指针的值。开启指针优化的情况下,在 arm64 架构中用33位用来存储类指针,x86下用44
magic 用于调试器判断当前对象是真的对象还是没有初始化的空间
weakly_referenced 标志对象是否被指向或者曾经指向一个 ARC 的弱变量,
deallocating 标志对象是否正在释放内存
has_sidetable_rc 当对象引用技术大于 10 时,则需要借用该变量存储进位

isa_t其实是一个联合体位域的结构,其关键的类信息存储在shiftcls中,其他都是一些辅助信息。既然关键的类信息都存储在shiftcls中,如何证明呢,下面我们通过获取类信息的流程来佐证。

三、获取类信息过程

平常我们在获取类信息时一般通过object_getClass方法,来看一下object_getClass这个方法的调用过程:

Class object_getClass(id obj)
{
    //调用对象的getIsa方法
    if (obj) return obj->getIsa();
    else return Nil;
}
inline Class 
objc_object::getIsa() 
{
    //自定义类都是非TaggedPointer类型的,所以直接调用ISA()方法
    if (fastpath(!isTaggedPointer())) return ISA();

    extern objc_class OBJC_CLASS_$___NSUnrecognizedTaggedPointer;
    uintptr_t slot, ptr = (uintptr_t)this;
    Class cls;

    slot = (ptr >> _OBJC_TAG_SLOT_SHIFT) & _OBJC_TAG_SLOT_MASK;
    cls = objc_tag_classes[slot];
    if (slowpath(cls == (Class)&OBJC_CLASS_$___NSUnrecognizedTaggedPointer)) {
        slot = (ptr >> _OBJC_TAG_EXT_SLOT_SHIFT) & _OBJC_TAG_EXT_SLOT_MASK;
        cls = objc_tag_ext_classes[slot];
    }
    return cls;
}
inline Class 
objc_object::ISA() 
{
    ASSERT(!isTaggedPointer()); 
//上面已经分析过,此处SUPPORT_INDEXED_ISA为0
#if SUPPORT_INDEXED_ISA
    if (isa.nonpointer) {
        uintptr_t slot = isa.indexcls;
        return classForIndex((unsigned)slot);
    }
    return (Class)isa.bits;
#else
    //所以最后获取类信息就是取bits & ISA_MASK
    return (Class)(isa.bits & ISA_MASK);
#endif
}
#   define ISA_MASK        0x00007ffffffffff8ULL

可以看到最后获取类信息就是取对象的bits & ISA_MASK的值,其实就是shiftcls中存储的信息,与上ISA_MASK(0x00007ffffffffff8ULL)就是为了把低3位和高17位置0,写个简单的demo看一下:

获取类信息

这里补充几个知识点:
x/4gx 打印4个8字节内存信息
p/x 以16进制打印
p/t 以2进制打印

//LGPerson *p = [LGPerson alloc];
(lldb) p/x p
(LGPerson *) $21 = 0x000000010070c920
(lldb) p/t p
(LGPerson *) $22 = 0b0000000000000000000000000000000100000000011100001100100100100000
(lldb) x/4gx p
0x10070c920: 0x001d800100003275 0x0000000000000000
0x10070c930: 0x0000000000000000 0x0000000000000000
(lldb) p/t 0x001d800100003275
(long) $24 = 0b0000000000011101100000000000000100000000000000000011001001110101
(lldb) p/t 0x00007ffffffffff8ULL
(unsigned long long) $25 = 0b0000000000000000011111111111111111111111111111111111111111111000
(lldb) p/x 0x001d800100003275 & 0x00007ffffffffff8ULL
(unsigned long long) $26 = 0x0000000100003270
(lldb) p 0x0000000100003270
(long) $27 = 4294980208
(lldb) po 0x0000000100003270
LGPerson

看完上面的整个流程后,大家可能还有一个疑问:为什么在赋值shiftcls的时候要把cls右移3位,而不是直接把cls的地址给它?
原因很简单:类其实是原类的实例对象,它的真实的信息也是存储在8字节的4-47位上的,右移3位才能给把这44位信息一一对应的赋值给shiftcls,而在获取类信息时把isa & 0x00007ffffffffff8ULL也是为了把低3位以及高17位(注意是在mac上)置0保留中间44位。

最后

上面的分析流程中也提到了对象、类、以及原类之间是存在一定关系的,通过上面的分析证明了对象和类之间关联性,但它们和原类之间有什么关系呢,这个问题就留待下次分析吧~
未完待续...

相关文章

  • isa构造那点事(三)

    背景 在上篇对象alloc那点事中分析对象创建过程,最终是走到_class_createInstanceFromZ...

  • oc类结构那点事(四)

    背景 在上篇isa构造那点事最后遗留了一个问题,oc对象在内存中与类以及原类之间的关系。另外前面一直在分析对象的创...

  • iOS时间那点事--NSDateFormatter

    文章出处 iOS时间那点事--NSDate iOS时间那点事--NSDateFormatter iOS时间那点事-...

  • 陪读时那点事|文集目录

    【陪读时那点事】01|学校旁的出租屋 【陪读时那点事】02|菜市场 【陪读时那点事】03|黑色球鞋 【陪读时那点事...

  • 第二讲 指令系统体系结构(Instruction Set Arc

    内容要点:x86 ISA,MIPS ISA 构造一台自己的计算机 指令格式 第一个字节第二个字节xxxx ...

  • Objective - C 对象的本质(四)isa与superc

    (一)isa指针 我们在前面几章一直提到isa指针,isa指针是三种对象中都有的成员变量,那么三种对象的isa指针...

  • iOS开发:设计模式那点事

    iOS开发:设计模式那点事 iOS开发:设计模式那点事

  • 初恋那点事(三)

    4月1日 那天,春光正好,月季花开的正艳。 “叶,手掌伸开。” “干嘛?” “给你样好东西。” “好看吧。” 我把...

  • 童年那点事(三)

    我们这个村子人住的屋子都建在沟边,依沟而建,我家在村子的西面。门前是一小段陡坡,走完陡坡还有大约五分地,来到地边就...

  • 人性那点事(三)

    今天特别想剖析人性的这个特点---嫉妒心。原因就是最近我的嫉妒心总是不停的冒出来,导致我对待事情不能有正确的判断,...

网友评论

      本文标题:isa构造那点事(三)

      本文链接:https://www.haomeiwen.com/subject/vhzpektx.html