iOS-isa结构探一探

2020-09-11  本文已影响0人  灰溜溜的小王子

本文仅记录笔者的学习过程,只代表笔者个人的理解,如果有错的地方,欢迎各位指正!

OC对象的本质

对象继承与NSObject翻看Objc源码可以看到

@interface NSObject <NSObject> {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-interface-ivars"
    Class isa  OBJC_ISA_AVAILABILITY;
#pragma clang diagnostic pop
}

可以看到Class类型的isa继续跟进去可以看到

typedef struct objc_class *Class;

可以得治Class其实是个结构体,继续找objc_class结构体的定义

struct objc_class {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;

#if !__OBJC2__
    Class _Nullable super_class                              OBJC2_UNAVAILABLE;
    const char * _Nonnull name                               OBJC2_UNAVAILABLE;
    long version                                             OBJC2_UNAVAILABLE;
    long info                                                OBJC2_UNAVAILABLE;
    long instance_size                                       OBJC2_UNAVAILABLE;
    struct objc_ivar_list * _Nullable ivars                  OBJC2_UNAVAILABLE;
    struct objc_method_list * _Nullable * _Nullable methodLists                    OBJC2_UNAVAILABLE;
    struct objc_cache * _Nonnull cache                       OBJC2_UNAVAILABLE;
    struct objc_protocol_list * _Nullable protocols          OBJC2_UNAVAILABLE;
#endif

} OBJC2_UNAVAILABLE;

从上面的源码中我们得知对象的本质是个结构体,且结构体中国呢的第一个参数是一个isa

isa是什么

这个isa也就是将要探索的内容。在笔者OC对象之alloc探索-源码探索的三种方式这篇博文中可以看到有这么一个初始化

static ALWAYS_INLINE id
_class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone,
                              int construct_flags = OBJECT_CONSTRUCT_NONE,
                              bool cxxConstruct = true,
                              size_t *outAllocatedSize = nil)
{
    ASSERT(cls->isRealized());

    // Read class's info bits all at once for performance
    bool hasCxxCtor = cxxConstruct && cls->hasCxxCtor();
    bool hasCxxDtor = cls->hasCxxDtor();
    bool fast = cls->canAllocNonpointer();
    size_t size;

    size = cls->instanceSize(extraBytes);
    if (outAllocatedSize) *outAllocatedSize = size;

    id obj;
    if (zone) {
        obj = (id)malloc_zone_calloc((malloc_zone_t *)zone, 1, size);
    } else {
        obj = (id)calloc(1, size);
    }
    if (slowpath(!obj)) {
        if (construct_flags & OBJECT_CONSTRUCT_CALL_BADALLOC) {
            return _objc_callBadAllocHandler(cls);
        }
        return nil;
    }
    //创建isa
    if (!zone && fast) {
        obj->initInstanceIsa(cls, hasCxxDtor);
    } else {
        // Use raw pointer isa on the assumption that they might be
        // doing something weird with the zone or RR.
        obj->initIsa(cls);
    }

    if (fastpath(!hasCxxCtor)) {
        return obj;
    }

    construct_flags |= OBJECT_CONSTRUCT_FREE_ONFAILURE;
    return object_cxxConstructFromClass(obj, cls, construct_flags);
}

其中有obj->initInstanceIsa(cls, hasCxxDtor)跟踪这个创建过程

objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor) 
{ 
    ASSERT(!isTaggedPointer()); 
    
    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
        // 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;
    }
}
断点定位 image.png
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是一个联合体位域结构那么什么是粘合体位域?

位域的使用

C语言中,位域的生命和结构体类似不同是:

  • 在声明时,位域成员必须是整形或枚举类型(通常是无符号类型)
  • 成员名的后面是一个冒号和一个整数,整数规定了成员所占用的位数
  • 位域不能是静态类型。不能使用&对位域做取地址运算,因此不存在位域的指针,编译器通常不支持位域的引用(reference)

分别创建位域和结构体对比:

struct Stuct {
    // (数据类型 元素);
    int  a;// 4字节 0 1 2 3
    char b;// 1字节 4。不够8字节补齐
}Stuct1;

struct Uni{
    // (数据类型 位域名: 位域长度);
    int a : 1;
    long b : 1;
}Uni1;
来看下效果: image.png
image.png

联合体

1.1 联合体特征
union PPP {
    int a;      //4个字节
    short b;    //2个字节
    char c;     //1个字节
} p;

NSLog(@"union size: %lu - %lu",sizeof(p), sizeof(union PPP));
结果:
union size: 4 - 4


union PPP {
    int a;      //4个字节
    short b;    //2个字节
    char c;     //1个字节
    long d;     //8个字节
} p;
结果
union size: 8 - 8

可见:联合体的内存是成员所需的最大内存那个。

1.2 联合体特征
 p.a = 2;
NSLog(@"%d---%d---%c---%d",p.a,p.b,p.c,p.d);//2---2---�---2
p.b = 4;
NSLog(@"%d---%d---%c---%d",p.a,p.b,p.c,p.d);//4---4---�---4
p.c = 'c';
NSLog(@"%d---%d---%c---%d",p.a,p.b,p.c,p.d);//99---99---c---99

可见:每次改变联合体中的成员,其他成员会受到影响、即联合体成员之间是相互互斥的

上一篇下一篇

猜你喜欢

热点阅读