iOS动态运行时runtime

iOS的动态运行时体现在:

  • 运行时根据对象isa指针来实现查找属性、方法等
  • 消息传递,运行时根据方法名SEL查找方法实现IMP,或进入消息转发流程
  • Method-Swizzling,方法名SEL与方法实现IMP交换
  • 动态添加方法
  • 动态方法解析
  • 运行时决议

消息传递

实例方法存储在类对象中,静态方法存储在元类对象中。
调用方法,会在编译阶段编译成如下代码:

 [self description];
 objc_msgSend(self, @selector(description)) # 编译后的代码

 [NSObject description];
 objc_msgSend([NSObject class], @selector(description)) # 编译后的代码

消息传递过程分为三个阶段:

  • 消息发送
  • 动态方法解析
  • 消息转发

以调用实例方法为例,运行时,根据实例对象的isa指针,找到类对象。类对象结构如下:

# 类对象结构
struct objc_class {
  Class isa;  # 指向元类对象
  Class superClass; # 指向父级类对象
  cache_t cache;  # 方法缓存
  class_data_bits_t bits; # 用于获取具体的类信息
}

类对象结构体的的bits跟实例对象的位域存储数据FAST_DATA_MASK进行&操作,即可得到结构体class_rw_tclass_rw_t中存储着实例方法列表、属性列表和协议列表。结构如下:

struct class_rw_t {
    // ...省略其他值
    # 方法列表 二维数组 [method_list_t, method_list_t, ...] , method_list_t数组包含了method_t
    method_list_t *methods;   
    # 属性列表 二维数组        
    property_list_t *properties;    
    # 协议列表 二维数组
    const protocol_list_t *protocols; 
}

实例方法列表中的method_list_t保存的method_t结构如下:

# method_t是对方法(函数)的封装
struct method_t {
    SEL name;           # 函数名
    const char *types;  # 编码(返回值类型、参数类型)
    IMP imp;            # 指向函数地址的指针
}

通过SEL查找方法的IMP顺序是:

  • 先在当前类中查找

    • 先查找方法缓存列表是否命中:通过给定的方法选择器SEL,通过一个函数(y = f(x)),经过哈希查找来映射出bucket_t在数组中的索引位置,如果方法缓存没有命中,再查找方法列表
    • 对于已经排序好的,采用二分查找算法,查找对应方法的执行函数
    • 对于未排序好的,采用一般遍历查找算法,查找对应方法的执行函数
  • 如果当前类中没有找到,根据superClass指针去父级类中查找

    • 先查找方法缓存列表是否命中
    • 方法缓存未命中,再查找方法列表是否命中
  • 如果逐级父类都没有找到该方法,进入动态方法解析流程

    • 对于类方法,可在+ (BOOL)resolveClassMethod:(SEL)sel中进行动态添加方法class_addMethod(),并返回YES
    • 对于实例方法,可在- (BOOL)resolveInstanceMethod:(SEL)sel中进行动态添加方法class_addMethod(),并返回YES
    • 动态方法解析实现了后,会重新走消息发送流程,从方法缓存开始重新查找
  • 如果动态方法解析未实现(返回了NO),则进入消息转发流程

    • 判断方法- (id)forwardingTargetForSelector:(SEL)aSelector是否返回了转发目标,如果返回了,消息转发流程结束,否则进入下一步
    • 判断方法- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector是否返回了方法签名,如果返回了进入下一步,否则抛出异常
    • 实现- (void)forwardInvocation:(NSInvocation *)anInvocation进行消息转发操作
# 实例方法动态解析
+ (BOOL)resolveInstanceMethod:(SEL)sel {
    if (sel == @selector(test)) {  # 要拦截的方法
        
        # 要动态添加的方法的IMP
        IMP imp = class_getMethodImplementation([self class], @selector(dynamicMethod));
        
        # 最后一个参数const char *types: 参数、返回值编码
        #”v@:”意思就是这已是一个void类型的方法,没有参数传入。
        # “i@:”就是说这是一个int类型的方法,没有参数传入。
        # ”i@:@”就是说这是一个int类型的方法,又一个参数传入。
 
        class_addMethod(self, @selector(test), imp, "v@:");
        return [super resolveInstanceMethod: sel];
    }
    return NO;
}

- (void)dynamicMethod {
    NSLog(@"afasf");
}

# 类方法动态解析
+ (BOOL)resolveClassMethod:(SEL)sel {
}

# 消息转发目标
- (id)forwardingTargetForSelector:(SEL)aSelector {
    if (aSelector == @selector(test)) {
        return [Cat new]; # 返回对象不会向下执行
    }
    return [super forwardingTargetForSelector: aSelector];  # 继续向下进行
}

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    # 需要返回方法签名 否则抛出异常
    return methodSignature;
}

- (void)forwardInvocation:(NSInvocation *)anInvocation {
    # 进行自定义转发操作
}


思考 super关键字

# Father类继承于NSObject  Son类继承于Father
# Son类初始化方法如下, 如果实例化Son对象,下面会如何打印❓
- (instancetype)init {
    if (self = [super init]) {
        
        NSLog(@"[self class] = %@", [self class]);
        NSLog(@"[self superclass] = %@", [self superclass]);
        
        NSLog(@"[super class] = %@", [super class]);
        NSLog(@"[super superclass] = %@", [super superclass]);
    }
    return self;
}

答案:

[self class] = Son
[self superclass] = Father
[super class] = Son
[super superclass] = Father

解析:
[self class]编译阶段会变成:

  * self是receiver 消息接受者

  objc_msgSend(self, @selector(class));

[super class]编译阶段会变成:

   * 第一个参数 结构体中的self是receiver 消息接受者
   * 第一个参数 结构体中的 [Father Class]是表示从父类Father开始查找方法

   objc_msgSendSuper( {self, [Father Class]} , @selector(class));

[self class]会从当前实例对象的类对象开始搜索class方法,一直搜索到根类NSObject类
[super class]会从当前实例对象的类对象的父类对象开始搜索class方法,一直搜索到根类NSObject类,与[self class]的不同只是开始搜索位置不同
class方法的内部实现:

- (Class)class{
    return object_getClass(self);
}

所以最终返回的结果取决于self,而self是消息调用者又叫消息接受者,即receiver

superClass的内部实现:

- (Class)superclass {
    return class_getSuperclass(object_getClass(self));
}
获取self的super 都是Father

你可能感兴趣的:(iOS动态运行时runtime)