美文网首页
iOS-Runtime01-基础结构isa以及相关知识(位运算、

iOS-Runtime01-基础结构isa以及相关知识(位运算、

作者: IBigLiang | 来源:发表于2019-08-27 15:46 被阅读0次

    在笔者编写的iOS-Objective-C的本质中,我们已经了解到,所有的OC对象本质中都包含了一个isa指针,这个指针要么指向类对象,要么指向元类对象,今天,我们进一步来观察下这个isa指针内部所包含的信息。
    既然要观察isa内部的信息,我们当然得去查看objc的源码,因为大家都知道isa底层的源码在Xcode中是无法看到的,笔者查看了一下最新的objc源码最新到了objc4-756.2.tar.gz
    下载之后,我们一起看下isa的底层结构吧。
    首先我们在Xcode中搜索下isa,第一个搜索到的就是

    Class isa  OBJC_ISA_AVAILABILITY;
    

    然后点击Class,看到的是

    typedef struct objc_class *Class;
    typedef struct objc_object *id;
    

    点击objc_class,发现它是一个结构体,而且继承objc_object

    struct objc_class : objc_object {
    ......
    }
    

    那我们就直接看objc_object:

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

    这里我们就发现了我们需要查找的目标isa_t isa;点击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_t是一个union,简称共用体。而且在这个地方有个if判断,如果定义了ISA_BITFIELD,则包含一个结构体。搜索一下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)
    

    由此可得,在arm64系统下,定义了ISA_BITFIELD这个结构体,这个结构体中用到的知识点就是位域,内容就是紧跟着它下面的那个部分,所以在arm64下,isa_t的整体结构如下:

    union isa_t {
        isa_t() { }
        isa_t(uintptr_t value) : bits(value) { }
    
        Class cls;
        uintptr_t bits;
        struct {
          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
        };
    };
    

    到这里,我们已经可以看到,OC底层在定义isa的时候,需要用到的知识点是union、位域,那所谓的位运算又在哪里呢?其实就是跟arm64系统下定义的以下几个宏有关系

    #   define ISA_MASK        0x0000000ffffffff8ULL
    #   define ISA_MAGIC_MASK  0x000003f000000001ULL
    #   define ISA_MAGIC_VALUE 0x000001a000000001ULL
    

    了解OC本质的同学可能知道,其实在很早之前,OC对象底层结构体的isa指针,是直接指向这个对象对应的类对象或者元类对象的地址,不过在之后的版本中,苹果针对isa指针做了优化,在得到对象的isa指针地址值之后需要&上一个ISA_MASK,才能真正得到这个对象对应的类对象或者元类对象的地址值。所以这里就涉及到了位运算,相信位运算大家都了解,这也是C语言的基础知识,总结的来说就是

    &与运算,任何值 &1就是本身,&0就是0
    | 或运算,任何值 | 1就是1,| 0就是本身
    1 & 1 => 1       1 | 1 => 1
    1 & 0 => 0      0 | 1 => 1
    0 & 0 => 0     0 | 0 => 0
    

    接下来就是位域的知识点,其实很简单,就比方说上述所定义:

    struct {
          uintptr_t nonpointer        : 1; /*位域*/                                 
          uintptr_t has_assoc         : 1;                                         
          uintptr_t has_cxx_dtor      : 1;                                         
          ......
        };
    

    每一个变量占有一位,那这么做的意义在哪里呢?其实就是可以节省内存空间,大家想想,如果我们拿一个bool类型来表示一个标志位,这样的话,每一个标志位都需要4个字节,那如果我们拿一个字节中的一个位来表示这个标志位,不也是可行的吗?因为本身一个位就是用1和0表示,不就刚好代表着true和false吗,就如以下这个定义:

    struct {
                char sex : 1;   /*性别,1表示男,0表示女*/
                char isLikeMike : 1;   /*是否喜欢牛奶*/
                char isThin : 1;   /*是否偏瘦*/
                char handsome : 1;   /*是否帅气*/
            } _personCharacteristic;   /*个人特征*/
    

    这个时候,我们就可以很大程度上节约的内存空间,因为每个特诊字段,我们都只需要一个位就可以搞定。至于如何取值赋值,这里暂时不做探讨,大家有空可以自己玩玩。
    接下来,我们在来看看union这个共用体是什么用法,它的大概意思就是大家一起共用一个空间,先举个简单的例子:

    union {
        int year;
        int month;
        int day;
    } _yearMonthDay;
    
    int main(int argc, const char * argv[]) {
        
        
        
        @autoreleasepool {
            
            _yearMonthDay.year = 2019;
            NSLog(@"year is %d -- month is %d -- day is %d", _yearMonthDay.year, _yearMonthDay.month, _yearMonthDay.day);
            
        }
        return 0;
    }
    

    打印出来的结果就是如下:

    year is 2019 -- month is 2019 -- day is 2019
    

    所以从这里,大家应该知道了union的用法。
    那最后一个问题就是union和struct一起用,会起到一个什么作用呢?我们回到isa_t的定义上来:

    union isa_t {
        isa_t() { }
        isa_t(uintptr_t value) : bits(value) { }
    
        Class cls;
        uintptr_t bits;
        struct {
          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
        };
    };
    

    我们可以看到isa_t中结构体部分的总大小是64位,uintptr_t这个其实是

    typedef unsigned long           uintptr_t;
    

    也是64位,所以结构体和bits是大小一致的 这样就符合了我们的共用体规则。这样的话,后面有关于所有结构体内位运算的处理,我们都可以用bits来代替,不需要把结构体中的每一个字段读取出来,而结构体的主要内容,就是使我们的isa_t的定义有很高的可读性。这也就是isa_t将union和struct结合使用的核心意义所在!
    还有一个补充的一点就是结构体内的各个字段的意义:


    WeChat9fb7a77bd10134dc221ad4e2d75623e6.png

    关于Runtime中isa的基本底层结构的分享暂时就到这里,后面笔者会继续分享有关于Runtime底层的其他更多的知识点,希望对大家有所帮助,如果分享的地方有偏差的,欢迎大家一起怒!!!😁

    相关文章

      网友评论

          本文标题:iOS-Runtime01-基础结构isa以及相关知识(位运算、

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