欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

位域-isa指针

程序员文章站 2022-05-27 22:52:02
一、isa指针结构 分析: 1.我们知道,实例对象的isa指针指向该对象所属类的类对象;类对象的isa指向其元类对象; 2.真机为arm64架构,模拟器和mac电脑为x86架架构,以下以arm64为例讲解; 3.在64位系统下,指针所占字节为8个即64位; 4.在arm64之前,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.存储信息介绍:

位域-isa指针

 

其中,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\n", bit.a, bit.b, bit.c);
    
    pbit=&bit;
    pbit-> a=0;
    pbit-> b&=3;
    pbit-> c|=1;
    printf("%d,%d,%d\n ",pbit-> a,pbit-> b,pbit-> c);
}

 

 

位域-isa指针

 

//输出

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\n", bit.a, bit.b, bit.c);
    
    pbit=&bit;
    pbit-> a=0;
    pbit-> b&=3;
    pbit-> c|=1;
    printf("%d,%d,%d\n ",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的情况;

 

三、结论

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

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

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

位域-isa指针

 

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

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

 

github