美文网首页
Runtime(一)

Runtime(一)

作者: comsubin | 来源:发表于2019-05-28 16:13 被阅读0次

    isa

    • arm64 之前,isa就是一个普通的指针,它指向classormeta-class
    • arm64之后,对isa进行了优化,变成了一个共同体(union)结构,还使用位域来存储更多信息.
      先来看一个例子:新建person
    @interface Person : NSObject
    
    - (void)setTall:(BOOL)tall;
    - (void)setRich:(BOOL)rich;
    - (void)setHandSome:(BOOL)handSome;
    
    - (BOOL)getTall;
    - (BOOL)getRich;
    - (BOOL)getHandSome;
    
    @end
    

    再来.m文件

    //掩码 进行位运算
    //#define ISTallMASK  1
    //#define ISRichMASK  2
    //#define ISHandSomeMASK  4
    
    #define ISTallMASK  (1<<0)
    #define ISRichMASK  (1<<1)
    #define ISHandSomeMASK  (1<<2)
    
    @interface Person()
    {
        char _tallRichHandsome;
    }
    
    @end
    
    @implementation Person
    
    - (instancetype)init
    {
        self = [super init];
        if (self) {
            //从右至左tall rich handsome
            _tallRichHandsome = 0b00000000;
        }
        return self;
    }
    
    - (void)setTall:(BOOL)tall{
        if (tall) {
            // | 或运算 有1则为1
            /*
                0000 0001
                0000 0001
             */
            _tallRichHandsome = _tallRichHandsome | ISTallMASK;
        }else{
            /*
                0000 0101
                1111 1110
                先取反 在进行&运算 保证其他为不变 标志位必为0
             */
            _tallRichHandsome = _tallRichHandsome & ~ISTallMASK;
        }
    }
    
    - (void)setRich:(BOOL)rich{
        if (rich) {
            _tallRichHandsome = _tallRichHandsome | ISRichMASK;
        }else{
            _tallRichHandsome = _tallRichHandsome & ~ISRichMASK;
        }
    }
    
    - (void)setHandSome:(BOOL)handSome{
        if (handSome) {
            _tallRichHandsome = _tallRichHandsome | ISHandSomeMASK;
        }else{
            _tallRichHandsome = _tallRichHandsome & ~ISHandSomeMASK;
        }
    }
    
    - (BOOL)getTall{
        //&运算  同为1则为1
        /*
         0000 0000
         0000 0001  // 十进制1*2^0 = 1
         */
        return !!(_tallRichHandsome & ISTallMASK);
    }
    
    - (BOOL)getRich{
        /*
            0000 0000
            0000 0010  // 十进制1*2^1 = 2
         */
        return !!(_tallRichHandsome & ISRichMASK);
    }
    
    - (BOOL)getHandSome{
        /*
         0000 0000
         0000 0100  // 十进制1*2^2 = 4
         */
        return !!(_tallRichHandsome & ISHandSomeMASK);
    }
    

    这样我们就实现了一个字节存储3个BOOL变量.如果以后要增加到4个BOOL变量,不免有些麻烦,接下来优化一下.

    @interface Person()
    {
    //    char _tallRichHandsome;
        //位域
        struct {
            char tall :1;//表示只占一位
            char rich :1;
            char handsome :1;
            
        }_tallRichHandsome;
    }
    
    @implementation Person
    
    - (void)setTall:(BOOL)tall{
        _tallRichHandsome.tall = tall;
    }
    
    - (void)setRich:(BOOL)rich{
      _tallRichHandsome.rich = rich;
    }
    
    - (void)setHandSome:(BOOL)handSome{
        _tallRichHandsome.handsome = handSome;
    }
    
    - (BOOL)getTall{
    //    BOOL isRet = _tallRichHandsome.tall
        //这里强制转换 一个BOOL 类型是占一个字节 tall这里是0x01 强转后变 1111 1111 = 255,所以这里需要2次取反拿到正确的值.
        return !!(_tallRichHandsome.tall);
    }
    
    - (BOOL)getRich{
        return !!(_tallRichHandsome.rich);
    }
    
    - (BOOL)getHandSome{
       return !!(_tallRichHandsome.handsome);
    
    @end
    
            Person *persn = [[Person alloc]init];
            [persn setTall:YES];
            [persn setRich:NO];
            [persn setHandSome:NO];
            NSLog(@"%d-- %d ---%d",persn.getRich,persn.getTall,persn.getHandSome);
            
    (lldb) p/x &(persn->_tallRichHandsome)
    ((anonymous struct) *) $0 = 0x000000010281f2a8
    (lldb) x 0x000000010281f2a8
    0x10281f2a8: 01 00 00 00 00 00 00 00 63 46 1b 41 ff 7f 00 00  ........cF.A....
    0x10281f2b8: 4a 97 fb 42 ff 7f 00 00 b9 be 52 45 ff 7f 00 00  J..B......RE....
    

    01 00 00 00 00 00 00 00 第2个十六进制位,转换成二进制是0000 0001说明tall被放在最右边.
    接下来看看苹果的做法

    @interface Person()
    {
    //    位域
        union{
    
            char bits;
            struct {
                char tall :1;
                char rich :1;
                char handsome :1;
                
            };
            
        }_tallRichHandsome;
    }
    
    @end
    
    

    union是一个共同体,bits占一个字节,struct也占一个字节.
    .m文件

    
    - (void)setTall:(BOOL)tall{
        if (tall) {
            _tallRichHandsome.bits = _tallRichHandsome.bits | ISTallMASK;
        }else{
            _tallRichHandsome.bits = _tallRichHandsome.bits & ~ISTallMASK;
        }
    }
    

    再来看isa的结构

    union isa_t 
    {
    
        Class cls;
        uintptr_t bits;
    #   define ISA_MASK        0x0000000ffffffff8ULL
    #   define ISA_MAGIC_MASK  0x000003f000000001ULL
    #   define ISA_MAGIC_VALUE 0x000001a000000001ULL
        struct {
            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;
        };
    }
    
    • nonpointer
      0,代表普通的指针,存储着Class、Meta-Class对象的内存地址
      1,代表优化过,使用位域存储更多的信息
    • has_assoc
      是否有设置过关联对象,如果没有,释放时会更快
    • has_cxx_dtor
      是否有C++的析构函数(.cxx_destruct),如果没有,释放时会更快
    • shiftcls
      存储着Class、Meta-Class对象的内存地址信息
    • magic
      用于在调试时分辨对象是否未完成初始化
    • weakly_referenced
      是否有被弱引用指向过,如果没有,释放时会更快
    • deallocating
      对象是否正在释放
    • extra_rc
      里面存储的值是引用计数器减1
    • has_sidetable_rc
      引用计数器是否过大无法存储在isa中
      如果为1,那么引用计数会存储在一个叫SideTable的类的属性中

    Class结构

    struct objc_class  {
        Class ISA;
        Class superclass;
        cache_t cache;             // formerly cache pointer and vtable
        class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags
    }
    

    bits & FAST_DATA_MASK 得到

    
    //类的初始信息
    struct class_ro_t {
        uint32_t flags;
        uint32_t instanceStart;
        uint32_t instanceSize;
    #ifdef __LP64__
        uint32_t reserved;
    #endif
    
        const uint8_t * ivarLayout;
        
        const char * name;
        method_list_t * baseMethodList;
        protocol_list_t * baseProtocols;
        const ivar_list_t * ivars;
    
        const uint8_t * weakIvarLayout;
        property_list_t *baseProperties;
    
        method_list_t *baseMethods() const {
            return baseMethodList;
        }
    };
    
    struct class_rw_t {
        // Be warned that Symbolication knows the layout of this structure.
        uint32_t flags;
        uint32_t version;
    
        const class_ro_t *ro;
    
        method_array_t methods;//方法列表
        property_array_t properties;//属性列表
        protocol_array_t protocols;//协议列表
    
        Class firstSubclass;
        Class nextSiblingClass;
    
        char *demangledName;
    {
    

    method_array_t是一个二维数组里面最终存放的是method_t

    struct method_t {
        SEL name;
        const char *types;
        IMP imp;
    }
    
    • name; 函数名
    • types; 编码(函数返回值类型,参数类型)
    • imp; 函数地址(指向函数的指针)

    SEL代表方法\函数名,一般叫做选择器,底层结构跟char *类似
    可以通过@selector()sel_registerName()获得
    可以通过sel_getName()NSStringFromSelector()转成字符串
    不同类中相同名字的方法,所对应的方法选择器是相同的

    iOS中提供了一个叫做@encode的指令,可以将具体的类型表示成字符串编码


    image.png image.png

    接下来看看cache_t结构

    struct bucket_t {
        cache_key_t _key;// SEL作为key
        IMP _imp;//函数的内存地址
    };
    
    struct cache_t {
        struct bucket_t *_buckets;//哈希表
        mask_t _mask;//哈希表长度 - 1
        mask_t _occupied;//已经缓存的方法数量
    }
    

    哈希表相关资料链接

    相关文章

      网友评论

          本文标题:Runtime(一)

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