美文网首页
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