• 位域-isa指针


    一、isa指针结构

    union isa_t 
    {
        isa_t() { }
        isa_t(uintptr_t value) : bits(value) { }
    
        Class cls;
        uintptr_t bits;
    
    #if SUPPORT_PACKED_ISA
    
        // extra_rc must be the MSB-most field (so it matches carry/overflow flags)
        // nonpointer must be the LSB (fixme or get rid of it)
        // shiftcls must occupy the same bits that a real class pointer would
        // bits + RC_ONE is equivalent to extra_rc + 1
        // RC_HALF is the high bit of extra_rc (i.e. half of its range)
    
        // future expansion:
        // uintptr_t fast_rr : 1;     // no r/r overrides
        // uintptr_t lock : 2;        // lock for atomic property, @synch
        // uintptr_t extraBytes : 1;  // allocated with extra bytes
    
    # if __arm64__
    #   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;
    #       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
        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;
    #       define RC_ONE   (1ULL<<56)
    #       define RC_HALF  (1ULL<<7)
        };
    
    # else
    #   error unknown architecture for packed isa
    # endif
    
    // SUPPORT_PACKED_ISA
    #endif
    
    
    #if SUPPORT_INDEXED_ISA
    
    # if  __ARM_ARCH_7K__ >= 2
    
    #   define ISA_INDEX_IS_NPI      1
    #   define ISA_INDEX_MASK        0x0001FFFC
    #   define ISA_INDEX_SHIFT       2
    #   define ISA_INDEX_BITS        15
    #   define ISA_INDEX_COUNT       (1 << ISA_INDEX_BITS)
    #   define ISA_INDEX_MAGIC_MASK  0x001E0001
    #   define ISA_INDEX_MAGIC_VALUE 0x001C0001
        struct {
            uintptr_t nonpointer        : 1;
            uintptr_t has_assoc         : 1;
            uintptr_t indexcls          : 15;
            uintptr_t magic             : 4;
            uintptr_t has_cxx_dtor      : 1;
            uintptr_t weakly_referenced : 1;
            uintptr_t deallocating      : 1;
            uintptr_t has_sidetable_rc  : 1;
            uintptr_t extra_rc          : 7;
    #       define RC_ONE   (1ULL<<25)
    #       define RC_HALF  (1ULL<<6)
        };
    
    # else
    #   error unknown architecture for indexed isa
    # endif
    
    // SUPPORT_INDEXED_ISA
    #endif
    
    };

    分析:

    1.我们知道,实例对象的isa指针指向该对象所属类的类对象;类对象的isa指向其元类对象;

    2.真机为arm64架构,模拟器和mac电脑为x86架架构,以下以arm64为例讲解;

    3.在64位系统下,指针所占字节为8个即64位;

    4.在arm64之前,isa就是一个普通的指针,存放着类(元类)对象的地址;之后,则需要&

    ISA_MASK掩码,才能获取到类(元类)对象的地址,此时isa指针为一个共用体,存储的信息不局限于类(元类)对象的地址;

    5.存储信息介绍:

    其中,shiftcls结构体成员变量(33位)用来存储类(元类)对象的地址;

    二、类(元类)对象的地址取值原理——位域

    1.结构体支持位域运算

    //代码

    struct bs {
        unsigned a : 9;//如果超过位域范围(511),则只取范围内的值,其他位(高位)丢弃
        unsigned b : 4;
        unsigned c : 3;
    }bit, *pbit;
    
    void test1()
    {
        bit.a = 512;//超过位域范围报警告
        bit.b = 10;
        bit.c = 7;
        NSLog(@"%d,%d,%d
    ", bit.a, bit.b, bit.c);
        
        pbit=&bit;
        pbit-> a=0;
        pbit-> b&=3;
        pbit-> c|=1;
        printf("%d,%d,%d
     ",pbit-> a,pbit-> b,pbit-> c);
    }

    //输出

    2019-10-08 18:22:37.051464+0800 SetAndGetsForMask[1966:248996] 0,10,7
    0,2,7
     Program ended with exit code: 0

    //分析

    1)unsigned即无符号整型,占4个字节;结构体中成员变量所占内存相互独立且连续;

    2)以a为例,所占位数为9位即0b111111111(十进制511),所以a的取值范围0~511,如果是512(二进制0b1000000000),由于只取低9位(000000000),所以取出值为0;

    3)按位与&:两个都为1运算结果为1,否则为0;按位或|:两个都为0运算结果为0,否则为1;

    2.参照isa,共用体套用结构体,一个char字符(一个字节)存储多个BOOL值并制定存储位置

    2.设置类属性BOOL值(setter and getter)

    //Person

    #import "Person.h"
    
    //mask即掩码,表示二进制数(0b开头)
    #define TallMask (1<<0)      //表示1左移0位:0b 0000 0001
    #define RichMask (1<<1)      //表示1左移1位:0b 0000 0010
    #define HandsomeMask (1<<2)  //表示1左移2位:0b 0000 0100
    
    //拓展:10<<3即在10对应的二进制数后添加3个0
    
    @interface Person()
    {
        char _saveBox;
    }
    
    @end
    
    @implementation Person
    
    - (instancetype)init
    {
        if (self = [super init]) {
            //用一个字节来存储三个变量:从最右往左依次为Tall、Rich、Handsome
            _saveBox = 0b00000101;
        }
        return self;
    }
    
    /*思路
     0000 0101(_saveBox)
    |0000 0001(掩码)
     ---------
     0000 0001(赋值tall为1)
     
     0000 0101
    &1111 1110(掩码取反)
     ---------
     0000 0100(赋值tall为0)
     
     1.如果赋的值为1,则按位或;
     2.如果赋的值为0,则掩码先取反,后按位与;
     */
    - (void)setTall:(BOOL)tall
    {
        if (tall) {
            _saveBox |= TallMask;
        } else {
            _saveBox &= ~TallMask;
        }
    }
    
    - (void)setRich:(BOOL)rich
    {
        if (rich) {
            _saveBox |= RichMask;
        } else {
            _saveBox &= ~RichMask;
        }
    }
    
    - (void)setHandsome:(BOOL)handsome
    {
        if (handsome) {
            _saveBox |= HandsomeMask;
        } else {
            _saveBox &= ~HandsomeMask;
        }
    }
    
    /*思路
     0000 0101
    &0000 0001
     ---------
     0000 0001(取出tall值)
     
     1.按位与,用掩码取出_saveBox中特定位;
     2.结果>=1,取反为0,再取反为1;同理,为0则双取反后为0;
     */
    - (BOOL)isTall
    {
        return !!(_saveBox & TallMask);
    }
    
    - (BOOL)isRich
    {
        return !!(_saveBox & RichMask);
    }
    
    - (BOOL)isHandsome
    {
        return !!(_saveBox & HandsomeMask);
    }
    
    @end

    //Student

    #import "Student.h"
    
    @interface Student()
    {
        /*思路
         1.用一个结构体来存放变量;
         2.结构体支持位域:按先后顺序,一个char字符一个字节(0b0000 0000),从最右至左依次为tall、rich、handsome;
         */
        struct {
            char tall : 1;//用一位来存储
            char rich : 1;
            char handsome : 1;
        }_tallRichHandsome;
    }
    
    @end
    
    @implementation Student
    
    - (void)setTall:(BOOL)tall
    {
        _tallRichHandsome.tall = tall;
    }
    
    - (void)setRich:(BOOL)rich
    {
        _tallRichHandsome.rich = rich;
    }
    
    - (void)setHandsome:(BOOL)handsome
    {
        _tallRichHandsome.handsome = handsome;
    }
    
    - (BOOL)isTall
    {
        return !!_tallRichHandsome.tall;//非0(包括负数)取反为0
    }
    
    - (BOOL)isRich
    {
        return !!_tallRichHandsome.rich;
    }
    
    - (BOOL)isHandsome
    {
        return !!_tallRichHandsome.handsome;
    }
    
    @end

    //Worker

    #import "Worker.h"
    
    #define TallMask (1<<0)//也可以左移6位,剩余位没用到
    #define RichMask (1<<1)
    #define HandsomeMask (1<<2)
    #define ThinMask (1<<3)
    
    @interface  Worker()
    {
        //苹果系统设计思路
        union {
            char bits;//一个字节存储结构体中的所有成员变量
            struct {//摆设用:位域,增加可读性
                char tall : 1;//占一位
                char rich : 1;
                char handsome : 1;
                char thin : 1;
            };
        }_tallRichHandsome;
    }
    
    @end
    
    @implementation Worker
    
    - (void)setTall:(BOOL)tall
    {
        if (tall) {
            NSLog(@"----%c", _tallRichHandsome.bits);
            _tallRichHandsome.bits |= TallMask;
        } else {
            _tallRichHandsome.bits &= ~TallMask;
        }
    }
    
    - (void)setRich:(BOOL)rich
    {
        if (rich) {
            _tallRichHandsome.bits |= RichMask;
        } else {
            _tallRichHandsome.bits &= ~RichMask;
        }
    }
    
    - (void)setHandsome:(BOOL)handsome
    {
        if (handsome) {
            _tallRichHandsome.bits |= HandsomeMask;
        } else {
            _tallRichHandsome.bits &= ~HandsomeMask;
        }
    }
    
    - (void)setThin:(BOOL)thin
    {
        if (thin) {
            _tallRichHandsome.bits |= ThinMask;
        } else {
            _tallRichHandsome.bits &= ~ThinMask;
        }
    }
    
    - (BOOL)isTall
    {
        return !!(_tallRichHandsome.bits & TallMask);
    }
    
    - (BOOL)isRich
    {
        return !!(_tallRichHandsome.bits & RichMask);
    }
    
    - (BOOL)isHandsome
    {
        return !!(_tallRichHandsome.bits & HandsomeMask);
    }
    
    - (BOOL)isThin
    {
        return !!(_tallRichHandsome.bits & ThinMask);
    }
    
    @end

    //main

    #import <Foundation/Foundation.h>
    #import "Person.h"
    #import "Student.h"
    #import "Worker.h"
    #import "Engineer.h"
    
    struct bs {
        unsigned a : 9;//如果超过位域范围(511),则只取范围内的值,其他位(高位)丢弃
        unsigned b : 4;
        unsigned c : 3;
    }bit, *pbit;
    
    void test1()
    {
        bit.a = 512;//超过位域范围报警告
        bit.b = 10;
        bit.c = 7;
        NSLog(@"%d,%d,%d
    ", bit.a, bit.b, bit.c);
        
        pbit=&bit;
        pbit-> a=0;
        pbit-> b&=3;
        pbit-> c|=1;
        printf("%d,%d,%d
     ",pbit-> a,pbit-> b,pbit-> c);
    }
    
    void test2()
    {
        Person *per = [[Person alloc] init];
        per.tall = NO;
        per.rich = NO;
        per.handsome = YES;
        NSLog(@"%d %d %d", per.isTall, per.isRich, per.isHandsome);
    }
    
    void test3()
    {
        Student *stu = [[Student alloc] init];
        stu.tall = YES;
        stu.rich = NO;
        stu.handsome = YES;
        NSLog(@"%d %d %d", stu.isTall, stu.isRich, stu.isHandsome);
    }
    
    void test4()
    {
        Worker *worker = [[Worker alloc] init];
    //    worker.tall = YES;
        worker.rich = NO;
        worker.handsome = NO;
        worker.thin = YES;
        NSLog(@"%d %d %d", worker.isThin, worker.isRich, worker.isHandsome);
    }
    
    void test5()
    {
        Engineer *engineer = [[Engineer alloc] init];
    //    engineer.age = 12;
    //    engineer.level = 6;
    //    engineer.workers = 5;
        
        //0b 1111 1111 1111 1111(十进制:65535)
        //0b 0010 1100 1110 1101(十进制:11501)
        engineer->_personalInfo.bits =11501;
        NSLog(@"%d %d %d", engineer.getAge, engineer.getLevel, engineer.getWorkers);
        //2019-10-08 16:42:09.612140+0800 SetAndGetsForMask[1488:127227] 7 16 8160
        //
    }
    
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            
            test1();
    //        test2();
    //        test3();
    //        test4();
    //        test5();
        }
        return 0;
    }

    //打印

    2019-10-09 10:42:04.998750+0800 SetAndGetsForMask[2513:316066] 0 0 1
    2019-10-09 10:42:04.999093+0800 SetAndGetsForMask[2513:316066] 1 0 1
    2019-10-09 10:42:04.999122+0800 SetAndGetsForMask[2513:316066] 1 0 0
    Program ended with exit code: 0

    //分析(以Worker为例)

    1)共用体中所有成员共同占用一块内存区,其大小等于最大那个成员所占字节数;

    2)Worker中的结构体并为定义变量,编译器不会计算其内存,仅是增加可读性;

    3)Worker中只有一个char型变量bits(占一个字节),故该共用体变量_tallRichHandsome也占一个字节;

    4)结构体的位域限制变量的取值范围(一位:即0或1),mask掩码规定该变量存储的位置(在哪一位上);

    3.设置类属性非BOOL类型(setter and getter)——限定变量值范围且指定存储位置

    //Engineer

    #import <Foundation/Foundation.h>
    
    NS_ASSUME_NONNULL_BEGIN
    
    //位域位置(变量值存储位置)
    #define AgeMask 0b00000111//最低三位存储
    #define LevelMask (1<<4)//低位往高位数,第5位存储
    #define WorkersMask 0b0001111111100000
    
    @interface Engineer : NSObject
    {
        @public
        union {
            int bits;
            struct {//位域范围(变量值范围)
                int age : 3;
                int level : 1;
                int workers : 8;
            };
        }_personalInfo;
    }
    
    //- (void)setAge:(int)age;
    //- (void)setLevel:(int)level;
    //- (void)setWorkers:(int)workers;
    
    - (int)getAge;
    - (int)getLevel;
    - (int)getWorkers;
    
    @end
    
    NS_ASSUME_NONNULL_END
    
    
    
    #import "Engineer.h"
    
    @implementation Engineer
    
    //- (void)setAge:(int)age
    //{
    //    self->_personalInfo.bits |= AgeMask;
    //}
    //
    //- (void)setLevel:(int)level
    //{
    //    self->_personalInfo.bits |= LevelMask;
    //}
    //
    //- (void)setWorkers:(int)workers
    //{
    //    self->_personalInfo.bits |= WorkersMask;
    //}
    
    - (int)getAge
    {
        return self->_personalInfo.bits & AgeMask;
    }
    
    - (int)getLevel
    {
         return self->_personalInfo.bits & LevelMask;
    }
    
    - (int)getWorkers
    {
         return self->_personalInfo.bits & WorkersMask;
    }
    
    @end

    //打印

    2019-10-09 11:08:14.617655+0800 SetAndGetsForMask[2630:349068] 5 0 3296
    Program ended with exit code: 0

    //说明

    1)掩码mask既可以直接用二进制(0b开头)或十六进制(0x开头)表示,也可以左移符号<<表示(一般用于位域为1的情况);

    2)掩码表示所占位数:1表示占住该位,0未占;并且所占位数应当是连续的,不存在两侧为1,中间为0的情况;

    三、按位或(叠加)

    typedef NS_OPTIONS(NSUInteger, UIViewAutoresizing) {
        UIViewAutoresizingNone                 = 0,
        UIViewAutoresizingFlexibleLeftMargin   = 1 << 0,
        UIViewAutoresizingFlexibleWidth        = 1 << 1,
        UIViewAutoresizingFlexibleRightMargin  = 1 << 2,
        UIViewAutoresizingFlexibleTopMargin    = 1 << 3,
        UIViewAutoresizingFlexibleHeight       = 1 << 4,
        UIViewAutoresizingFlexibleBottomMargin = 1 << 5
    };
    
    
    self.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleRightMargin;

    1.分析:在iOS中,对view的自动布局经常会用到上述代码,苹果的做法是定义个枚举且每种类型都只占不同的一个二进制位,按位或相当于设定的场景都要考虑到;

    2.如下代码:

    typedef enum{
        YBOptionTypeNone = 0,   //0b0000
        YBOptionTypeOne = 1<<0, //0b0001
        YBOptionTypeTwo = 1<<1, //0b0010
        YBOptionTypeThree = 1<<2, //0b0100
        YBOptionTypeFour = 1<<3  //0b1000
    }YBOptionType;
    
    void test6(YBOptionType option)
    {
        /*按位或叠加(包含多种情况),按位与取出(获得特定情况)
         0b0001
         0b0100
       | 0b1000
      ----------
         0b1101
       & 0b0100
      ----------
         0b0100
         */
        if (option & YBOptionTypeOne) {
            NSLog(@"contain YBOptionTypeOne");
        }
        
        if (option & YBOptionTypeTwo) {
            NSLog(@"contain YBOptionTypeTwo");
        }
        
        if (option & YBOptionTypeThree) {
            NSLog(@"contain YBOptionTypeThree");
        }
        
        if (option & YBOptionTypeFour) {
            NSLog(@"contain YBOptionTypeFour");
        }
    }
    
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            
    //        test1();
    //        test2();
    //        test3();
    //        test4();
    //        test5();
            test6(YBOptionTypeOne | YBOptionTypeThree | YBOptionTypeFour);
        }
        return 0;
    }

    //打印

    2019-10-22 21:55:17.154998+0800 SetAndGetsForMask[996:46994] contain YBOptionTypeOne
    2019-10-22 21:55:17.155318+0800 SetAndGetsForMask[996:46994] contain YBOptionTypeThree
    2019-10-22 21:55:17.155332+0800 SetAndGetsForMask[996:46994] contain YBOptionTypeFour
    Program ended with exit code: 0

    四、结论

    1.arm64之后,isa是一个共用体类型的指针,存储内部套用的结构体中的所有成员变量;

    2.根据结构体的位域来限制成员变量的值范围,用掩码来规定成员变量存储的位置,对掩码按位与运算取出特定位置的成员变量的值;

    如:用bits对ISA_MASK按位与运算后,得到的是类(元类)对象的地址;

    可以看到shiftcls成员变量位域为33位,所占bits变量的存储位置为:地位到高位第四位起,最低三位是空出来的

    ————因此,在arm64架构中,所有的类和元类对象地址二进制表示时最低三位都为0,十六进制表示时最低一位为0或8(这个用class和object_getClass去打印地址,此处不再展示了)!

    3.按位与作用为在集合中取出某个特定值,按位或则将若干个特定值集合到一个值中(即所有设定场景都要考虑到);

    拓展:

    //代码

    //计算器演示

    //分析

    1.模拟器为x86_64架构,64位指针占八个字节;在结构体中,自上而下的变量在isa中的存储位置为从低位到高位即计算器中下到上、左到右——这点应该没问题;

    2.per实例对象曾经被弱指针指向过(现在释放了),且曾经关联过对象(现在置空),而对应的位为1即表示确实曾经被弱指针指向过(如果没有则会为0),曾经关联过对象;所以

    has_assoc和weakly_referenced分别表示对象曾经被弱指针指向过和曾经关联过对象——其他变量分析以此类推;

    GitHub

  • 相关阅读:
    听说这是互联网时代100本必读书单,你看过几本?
    HttpURL连接远程serverGet和Post方式请求并返回数据
    如何才干高速成为优秀的程序猿
    android学习笔记NO.5
    Linux 内核开发
    scikit-learn:3.3. Model evaluation: quantifying the quality of predictions
    多个client与一个server端通信的问题
    [Android Studio] 取消引用库打包出现异常-- provided dependencies can only be jars
    2016.3.16__CSS3_选择器_边框_背景_蒙版mask__第九天
    Android Studio:Multiple dex files define Landroid/support/annotation/AnimRes
  • 原文地址:https://www.cnblogs.com/lybSkill/p/11641510.html
Copyright © 2020-2023  润新知