美文网首页
runtime第三次学习,感谢MJ老师

runtime第三次学习,感谢MJ老师

作者: yulekwok | 来源:发表于2020-11-02 00:35 被阅读0次

    runtime

    工作中比较重要 动态性比较强

    提供了一套C语言的api 源码由C C++ 汇编语言编写实现

    isa 指针

    介绍

    1. 在arm64之前 isa 就是一个普通的指针,存储着Class Meta-Class 对象的内存地址

    2. 在arm64之后是将isa & ISA_MASK 找到真正的地址

      1. 现在是一个union 共用体 {class,bits,struct}使用位域来存储更多的信息 - 结构体支持位域
    union isa_t 
    {
        isa_t() { }
        isa_t(uintptr_t value) : bits(value) { }
    
        Class cls;
        uintptr_t bits;
        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;
    
        };
    }
    

    & 按位与 取出设置特定位置的值

    设置源码为 0111 MJ 老师的帅富高 改成 帅 不富有 高 0101

    1. 看看是不是富有 0101 & 0010 (想取出最高位那么就是最高位为1 其他的位为0)那么得出来的值是 0000 不富有
    2. 设置这个人不帅将某一位置为0 将这个位置置为0 其他位置为1 : 0101 & 1011 = 0001
    3. |按位或 设置特定位置的值 设置这个人是富有的 0101 | 0010 = 0111 的出来的结果
    union 值 中文说明
    nonpointer 0:代表普通的指针,存储着Class Meta-Class对象的内存地址1:代表优化过,使用位域存储更多的信息
    has_assoc 是否有设置过关联的对象,如果没有,释放时更快(category 增加属性)
    has_cxx_dtor 是否有C++的析构函数(.cxx_destruct),如果没有,释放时更快
    shiftcls 存储着Class,Meta-Class对象的内存地址信息
    magic 用于在调试时,分辨对象完成初始化
    weakly_referenced 是否被若引用指向过,如果没有释放时更快
    deallocating 对象是否正在释放
    has_sidetable_rc 引用计数器是否过大无法存储在isa中,如果为1,那么引用计数会存储在一个叫SideTable的类的属性中
    extra_rc 里面存储的值时引用计数器减1

    class 结构

    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 可读可写

    class_rw_t 里面包含着 methods、properties、protocols是二维数组,是可读可写的,包含了累的初始化内容,分类的内容

    class 中的bits & FAST_DATA_MASK -> 
    class_rw_t* data() {
        return (class_rw_t *)(bits & FAST_DATA_MASK);
    }
    class_rw_t -> 
    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; // 方法列表里面是一个二维数组 类方法 分类方法 //【method_list_t,method_list_t,method_list_t】 method_list_t:[method_t,method_t,method_t] 下面有解释
        property_array_t properties;//【property_list_t,property_list_t,property_list_t】property_list_t:[property_t,property_t,property_t]
        protocol_array_t protocols; // 【protocol_list_t,protocol_list_t,protocol_list_t】protocol_list_t:[protocol_ref_t,protocol_ref_t,protocol_ref_t]
    
        Class firstSubclass;
        Class nextSiblingClass;
    
        char *demangledName;
    }
    
    method_array_t -> class method_array_t : public list_array_tt<method_t, method_list_t>  // 
    struct method_t { // 方法信息
        SEL name;
        const char *types;
        IMP imp;
    }
    struct property_t { //属性
        const char *name;
        const char *attributes;
    };
    typedef uintptr_t protocol_ref_t;// typedef unsigned long           uintptr_t;  协议
    

    class_ro_t 只读

    class_ro_t 里面的baseMethodList、baseProtocols、ivars、baseProperties 是一维数组、是只读的、包含了类的初始化内容(仅仅是类里面定义的,不包含分类啥的)

    //class_ro_t -> readOnly 可读不可写
      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;// 【method_t,method_t,method_t】
        protocol_list_t * baseProtocols;
        const ivar_list_t * ivars; // 成员变量列表
    
        const uint8_t * weakIvarLayout;
        property_list_t *baseProperties;
    
        method_list_t *baseMethods() const {
            return baseMethodList;
        }
    };
    

    程序启动时会将 class_ro_t 里面的方法和分类添加到class_rw_t 中

    /* realizeClass
    * Performs first-time initialization on class cls, 
    * including allocating its read-write data.
    * Returns the real class structure for the class. 
    * Locking: runtimeLock must be write-locked by the caller
    *实现类
    *对类cls进行首次初始化,
    *包括分配其读写数据。
    *返回该类的真实类结构。
    *锁定:runtimeLock必须由调用方写锁定
    生成class_rw_t
    */
    static Class realizeClass(Class cls)
    

    method_t 是对方法\函数的封装

    struct method_t { // 方法信息
        SEL name; // 函数名
        const char *types; // 编码(返回值类型,参数类型)
        IMP imp;// 指向函数的指针(函数指针) +load() 就是直接调用的指针,不是消息转发
    }
    
    

    SEL

    // IMP 代表函数的具体实现
    typedef id _Nullable (*IMP)(id _Nonnull, SEL _Nonnull, ...); 
    // SEL 代表方法\函数,一般叫做选择器,底层结构和char * 类似
    typedef struct objc_selector *SEL; // 但是源码中没有对objc_selector的解释
    //GNU OC  但是和iOS的上的其实是不一样的
    typedef struct objc_selector *SEL;
           
    struct objc_selector
    {
      void *sel_id;
      const char *sel_types;
    };
    
    1. 获取SEL 的方法 @selector(init); sel_registerName("init");
    2. 将sel转成字符串 sel_getName(); NSStringFromSelector()
    3. 不同类中相同名字的方法,所对应的方法选择器是相同的

    **types ** 编码(返回值类型,参数类型)Type Encoding 简书

    例如 "i24@0:8i16f20"

    -(int)test:(int)age height:(float)height{ ->后面其实是有self 和 _cmd

    }

    -(int)test:(id)self cmd:(SEL)cmd age(int):age height:(float)height{

    }

    i 24 @ 0 : 8 i 16 f 20
    返回值int 所有的字节的总和 id +sel+int+float id 类型就是那个self(8字节) 从0开始 sel(8字节) 从第8个字节开始 int(4字节) 从16个字节开始 float(4字节) 从20个字节开始
    返回值 参数1 参数2 .... 参数n
    @encode(char)    c
    @encode(int)     i
    @encode(long)    q
    @encode(long long)   q
    @encode(unsigned char)   C
    @encode(unsigned int)    I
    @encode(unsigned short)  S
    @encode(unsigned long)   Q
    @encode(unsigned long long)  Q
    @encode(float)   f
    @encode(float *)     ^f
    @encode(double)  d
    @encode(double *)    ^d
    @encode(BOOL)    B
    @encode(void)    v
    @encode(void *)  ^v
    @encode(char *)  *
    @encode(NSObject)    {NSObject=#}
    @encode(NSObject *)  @
    @encode([NSObject class])    #
    @encode(SEL)     :
    @encode(intArray)    [3i]
    @encode(floatArray)  [3f]
    @encode(Struct)  {_struct=sqQ^d}
    @encode(NSError)     {NSError=#^vq@@}
    @encode(NSError ** )     ^@
    

    方法缓存

    Class 内部结构中有个方法缓存(cache_t),用散列表来缓存曾经调用过的方法,可以提高方法的查找速度

    牺牲空间换时间(散列表也是hash表)

    struct cache_t {
        struct bucket_t *_buckets; // 散列表[bucket_t,bucket_t,bucket_t,....]
        mask_t _mask; // 散列表长度 -1
        mask_t _occupied; // 已经缓存的方法的数量
    };
    struct bucket_t {
        cache_key_t _key; // SEL 作为key
        IMP _imp; // 函数的内存地址
    }
    // 存放地址为 sel&mask
    
    bucket_t * cache_t::find(cache_key_t k, id receiver)
    {
        assert(k != 0);
    
        bucket_t *b = buckets();
        mask_t m = mask();
        mask_t begin = cache_hash(k, m);
        mask_t i = begin;
        do {
            if (b[i].key() == 0  ||  b[i].key() == k) {// 如果找到的key 是想要的
                return &b[i];
            }
        } while ((i = cache_next(i, m)) != begin);// 不是
    
        // hack
        Class cls = (Class)((uintptr_t)this - offsetof(objc_class, cache));
        cache_t::bad_cache(receiver, (SEL)k, cls);
    }
    static inline mask_t cache_next(mask_t i, mask_t mask) {
        return i ? i-1 : mask; // 将值减1 如果是0 那么就变成mak 从最后再往前找
    }
    void cache_t::expand() //扩展方法列表 ,将之前的缓存清除掉(不会放回到新的中),然后再散列
    {
        cacheUpdateLock.assertLocked();
        
        uint32_t oldCapacity = capacity();
        uint32_t newCapacity = oldCapacity ? oldCapacity*2 : INIT_CACHE_SIZE; // 每次乘2倍 默认为4
    
        if ((uint32_t)(mask_t)newCapacity != newCapacity) {
            // mask overflow - can't grow further
            // fixme this wastes one bit of mask
            newCapacity = oldCapacity;
        }
    
        reallocate(oldCapacity, newCapacity);// 之前的缓存清除掉,然后再散列
    }
    enum {
        INIT_CACHE_SIZE_LOG2 = 2,
        INIT_CACHE_SIZE      = (1 << INIT_CACHE_SIZE_LOG2) //4
    };
    // 取值的主要函数
    static inline mask_t cache_hash(cache_key_t key, mask_t mask) 
    {
        return (mask_t)(key & mask);
    }
    

    方法调用流程

    image
    //OC 的方法调用:消息机制,给方法调用者发送消息
    //消息接收者(receiver)
    //消息名称
    
    

    objc_msgSend()

    1. 消息发送
    2. 动态方法解析
    3. 消息转发

    如果方法找不到方法最后会报错:

    unrecognized selector send to instance

    消息发送流程

    image
    // 二分查找 如果方法已经进行了排序,那么就进行二分查找
        for (count = list->count; count != 0; count >>= 1) {
            probe = base + (count >> 1);
            
            uintptr_t probeValue = (uintptr_t)probe->name;
            
            if (keyValue == probeValue) {
                // `probe` is a match.
                // Rewind looking for the *first* occurrence of this value.
                // This is required for correct category overrides.
                while (probe > first && keyValue == (uintptr_t)probe[-1].name) {
                    probe--;
                }
                return (method_t *)probe;
            }
            
            if (keyValue > probeValue) {
                base = probe + 1;
                count--;
            }
        }
    

    动态方法解析

    image
    - (void)other {
        NSLog(@"%s", __func__);
    }
    
    void c_other(id self, SEL _cmd)
    {
        NSLog(@"%s", __func__);
    }
    
    struct method_t { // 方法信息
        SEL name; // 函数名
        const char *types; // 编码(返回值类型,参数类型)
        IMP imp;// 指向函数的指针(函数指针) +load() 就是直接调用的指针,不是消息转发
    };
    + (BOOL)resolveClassMethod:(SEL)sel {
        // 动态添加方法实现
        if (sel == @selector(test)) {
            // 对象方法添加的位置是类对象上
            // 获取其他方法
            // 注意一定要加元类的
    
            { // 方法1
                struct method_t *method = (struct method_t *)class_getInstanceMethod(self, @selector(other));
    
                class_addMethod(object_getClass(self), sel, method->imp, method->types);
            }
            { // 方法2
                Method otherMethod = class_getInstanceMethod(self, @selector(other));
                class_addMethod(object_getClass(self), sel, method_getImplementation(otherMethod), method_getTypeEncoding(otherMethod));
            }
            {// 方法3
                class_addMethod(object_getClass(self), sel, (IMP)c_other, "v16@0:8");
            }
    
            // 返回yes 代表有动态添加方法 // 其实返回NO也是可以的 因为源码中拿着返回值仅仅是做的打印
            return YES;
        }
        return [super resolveInstanceMethod:sel];
    }
    
    + (BOOL)resolveInstanceMethod:(SEL)sel {
        // 动态添加方法实现
        if (sel == @selector(test)) {
            // 对象方法添加的位置是类对象上
            // 获取其他方法
    
            { // 方法1
                struct method_t *method = (struct method_t *)class_getInstanceMethod(self, @selector(other));
    
                class_addMethod(self, sel, method->imp, method->types);
            }
            { // 方法2
                Method otherMethod = class_getInstanceMethod(self, @selector(other));
                class_addMethod(self, sel, method_getImplementation(otherMethod), method_getTypeEncoding(otherMethod));
            }
            {// 方法3
                class_addMethod(self, sel, (IMP)c_other, "v16@0:8");
            }
    
            // 返回yes 代表有动态添加方法 // 其实返回NO也是可以的 因为源码中拿着返回值仅仅是做的打印
            return YES;
        }
        return [super resolveInstanceMethod:sel];
    }
    

    相关文章

      网友评论

          本文标题:runtime第三次学习,感谢MJ老师

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