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

2020-11-02  本文已影响0人  yulekwok

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];
}
上一篇下一篇

猜你喜欢

热点阅读