OC 对象原理探索(三):对象的本质 & isa

2021-07-16  本文已影响0人  SpringSunLcy

1. 联合体、位域

1.1 结构体

先看下面的代码:

struct SSLCar {
    BOOL front;
    BOOL back;
    BOOL left;
    BOOL right;
}sslCar;

NSLog(@"sslCar:%lu",sizeof(sslCar));

打印结果:sslCar:4

我们看到一个SSLCar结构体是4个字节,也就是32位(0000 0000 0000 0000 0000 0000 0000 0000),而SSLCar结构体中4BOOL值只需要4位(1111)就可以存储,这造成了非常大的空间浪费。

1.2 位域

通过位域指定每个成员变量占1位:

struct SSLCar2 {
    BOOL front: 1;
    BOOL back : 1;
    BOOL left : 1;
    BOOL right: 1;
}sslCar2;

NSLog(@"sslCar2:%lu",sizeof(sslCar2));

打印结果:sslCar2:1

此时内存空间已经得到了不错的优化。

1.3 联合体

创建联合体:

union SSLPerson {
    char *name;
    int  age;
};

为联合体赋值,并打印:

image

通过打印结果,发现联合体只有后赋值的变量才有值。

1.4 结构体和联合体的区别

结构体(struct)中所有变量是“共存”的——优点是“有容乃大”,全面;缺点struct内存空间的分配是粗放的,不管用不用,全分配。

联合体(union)中是各变量是“互斥”的——缺点就是不够“包容”; 但优点是内存使用更为精细灵活,也节省了内存空间。

2. 对象本质

main.m中添加如下代码:

@interface SSLPerson : NSObject

@property (nonatomic, copy) NSString *sslName;

@end

@implementation SSLPerson

@end


int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
    }
    return 0;
}

通过clang命令编译main.m(更多clang信息点这里):

clang -rewrite-objc main.m -o main.cpp

得到main.cpp文件,在main.cpp中我们可以看到有关对象的信息:

typedef struct objc_class *Class; // 7658行

struct objc_object { // 7661 行
    Class _Nonnull isa __attribute__((deprecated));
};

typedef struct objc_object *id; // 7666行

typedef struct objc_object SSLPerson; // 111788行

struct SSLPerson_IMPL { // 111793行
    struct NSObject_IMPL NSObject_IVARS;
    NSString *_sslName;
};

由此我们可以得出,对象是一个objc_object类型的结构体,内部有一个Class类型的isa指针。

打开源码,查看objc_object

struct objc_object {
private:
    isa_t isa;
    ...
}

3. isa 结构分析

我们在 OC 对象原理探索(一) 中,提到过obj->initIsa(cls),它的作用是将指针和类进行关联,下面我们用源码进行详细的分析。

initIsa函数:

inline void 
objc_object::initIsa(Class cls)
{
    initIsa(cls, false, false);
}

objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor) {
    ASSERT(!isTaggedPointer()); 
    
    isa_t newisa(0); // isa初始化

    if (!nonpointer) {
        newisa.setClass(cls, this);//如果是纯指针 isa被直接cls赋值
    } else { 
       ASSERT(!DisableNonpointerIsa);
       ASSERT(!cls->instancesRequireRawIsa());
#if SUPPORT_INDEXED_ISA
        ASSERT(cls->classArrayIndex() > 0);
        newisa.bits = ISA_INDEX_MAGIC_VALUE;
        newisa.has_cxx_dtor = hasCxxDtor;
        newisa.indexcls = (uintptr_t)cls->classArrayIndex();
#else   
        newisa.bits = ISA_MAGIC_VALUE;
#   if ISA_HAS_CXX_DTOR_BIT
        newisa.has_cxx_dtor = hasCxxDtor;
#   endif
        newisa.setClass(cls, this);
#endif
        newisa.extra_rc = 1;
    }
    isa = newisa;
}

点击查看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)

看一下isa的内存分布图:

image

总结:

4. isa 关联类

先创建一个SSLperson类,初始化[SSLPerson alloc],在initIsa函数中断点调试:

image.png

如上图,创建了一个联合体newisa,里边的变量目前都是0,没有赋值。

继续向下运行:

image.png

如上图,newisa.bits = ISA_MAGIC_VALUEISA_MAGIC_VALUE是一个宏
=0x001d800000000001。被赋值的变量有bits=8303511812964353cls=0x001d800000000001nonpointer=1magic=59。转化成二进制表示:

image.png

断点进入setClass

image.png

继续向下运行:

image.png
image.png

到此,isa关联的探索基本完成,以后如有更深入理解会持续更新。

上一篇下一篇

猜你喜欢

热点阅读