isa优化

作者: 如果大雨落下 | 来源:发表于2020-07-22 14:04 被阅读0次

& 运算

1>>0
1>>1
1>>2
1>>3

相当于将1 0b 0000 0001,= 1
如果左移1为,会变为 0b 0000 0010,= 2
左移2,会变为 0b 0000 0100, = 8

如果需要取出里面的值,就通过按位&,即用1 ,0000 0001 与传进来的值进行&,如果得到的结果不是0,就代表传进来是对应的值

NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld

 [_webView.scrollView addObserver:self forKeyPath:@"contentSize" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];

这个里面的或,其实就是将 0000 0001 和 0000 0010 进行或运算,得到的结果就是 0000 0011,这样在通过上面的方式进行&运算的时候,就能将这两个都能取出来了,这个前提就是对应的值必须要设置的巧妙

union

共用体,就代表里面的东西是公用一块内存
union {
char *bits;
struct {
int age:1;
int no:1;
int hev:1;
}
}

这个共用体总共只占用了4个字节,其中bits是4个字节,struct里面的冒号代表的是“位域”,即age,no,hev都只占用了一个自己,总共只占用了3个,因为共用内存,所以总共占用了4个字节

arm-64之后,isa指针升级,用的就是union

  • 所有类对象,元类对象地址的后3位都是0
struct objc_class : objc_object {
    // 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

    class_rw_t *data() const {
        return bits.data();
    }
    void setData(class_rw_t *newData) {
        bits.setData(newData);
    }

没有直接的isa指针了

 class_rw_t* data() const {
        return (class_rw_t *)(bits & FAST_DATA_MASK);
    }
    void setData(class_rw_t *newData)
    {
        ASSERT(!data()  ||  (newData->flags & (RW_REALIZING | RW_FUTURE)));
        // Set during realization or construction only. No locking needed.
        // Use a store-release fence because there may be concurrent
        // readers of data and data's contents.
        uintptr_t newBits = (bits & ~FAST_DATA_MASK) | (uintptr_t)newData;
        atomic_thread_fence(memory_order_release);
        bits = newBits;
    }

    // Get the class's ro data, even in the presence of concurrent realization.
    // fixme this isn't really safe without a compiler barrier at least
    // and probably a memory barrier when realizeClass changes the data field
    const class_ro_t *safe_ro() {
        class_rw_t *maybe_rw = data();
        if (maybe_rw->flags & RW_REALIZED) {
            // maybe_rw is rw
            return maybe_rw->ro();
        } else {
            // maybe_rw is actually ro
            return (class_ro_t *)maybe_rw;
        }
    }

通过bit & FAST_DATA_MASK 来获得class_rw_t,
在通过class_rw_t 获得

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;

里面就可以获取到methodlist,ivars,protocols等类的具体信息

method

方法需要通过一层层的查找(isa-》类对象-〉元类对象)
如果方法存储在元类对象,每次都要遍历查找,就会很耗时
所以方法有缓存

cache_t cache;     
 
struct cache_t {
#if CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_OUTLINED
    explicit_atomic<struct bucket_t *> _buckets;
    explicit_atomic<mask_t> _mask;
#elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_HIGH_16
    explicit_atomic<uintptr_t> _maskAndBuckets;
    mask_t _mask_unused;

里面的_buckets 是散列表(哈希表)
每调用一个方法,就会往里面加入一个方法

_maskAndBuckets 为散列表长度-1,
_mask_unused 表示已经存储的个数

散列表的存储,是通过@selector(xxx)与 _maskAndBuckets 做& ,获得一个索引,然后直接将IMP存储在这个索引下,如果算的这个索引下有东西了,就将索引减1,得到下一个位置,如果等于0了,那么索引就等于_maskAndBuckets,如果存储的个数超过了散列表本身的大小(一般初始大小是4),就清空整个散列表,然后对散列表进行扩容,在插入刚刚的方法

哈希表,就是通过传进来的值与一个特定的值通过算法获取索引,这样会导致空间的浪费,就是通过空间来换取时间的存储方式,但是查询速度会加快

下面是一些位的意思

位   名称  意义
1 bit   indexed     0 is raw isa, 1 is non-pointer isa.
1 bit   has_assoc   对象拥有或者曾经拥有一个相关的参考。对象没有关联的引用可以释放的更快。
1 bit   has_cxx_dtor    对象有一个 C++或者 ARC 的析构函数。没有析构函数的对象可以被释放的更快
30 bit  shiftcls    类指针的非零位 Class pointer's non-zero bits.
9 bit   magic   等于 0xd2。被调试器用来从没初始化的东西中区分真是的对象
1 bit   weakly_referenced   对象正被或者曾经被一个自动释放的弱引用变量指向。没有被弱引用的对象能够释放的更快。
1 bit   deallocating    对象正在被释放
1 bit   has_sidetable_rc    对象引用计数过大无法被 store inline
19 bit  extra_rc    对象的引用计数是extra_rc 值+1(举例,如果 extra_rc 是5,那么对象的真是引用计数是6)

相关文章

网友评论

      本文标题:isa优化

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