Runtime系列:消息机制【04】

OC中的方法调用的本质,都是转换为objc_msgSend函数的调用。
这里所说的消息机制就是objc_msgSend的执行流程。

objc_msgSend的执行流程可以分为3大阶段:

1、消息发送

1、首先判断消息接收者是否为nil,如果为nil则直接退出。所以存在使用 nil 调用方法,编译的时候也不会报错。
2、如果消息接收者不为空,通过isa指针找到消息接收者类对象,然后去查找方法(如果类对象没有找到方法,通过superclass指针找到父类继续查询),具体流程如下图:

receiver通过isa指针找到receiverClass

在这里插入图片描述
注意:如果是在class_rw_t中查找方法:

1、已经排序的,二分查找
2、没有排序的,遍历查找

如果消息接收者的类和所有父类中都没有找到方法实现。则进入动态方法解析阶段。

2、动态方法解析

底层实现代码:

_class_resolveMethod(cls,sel,inst);

triedResolver:是否动态解析的标记。
在这里插入图片描述

2.1、实例方法动态解析

方法一:

//-(void)test {
    
    
//    NSLog(@"%s",__func__);
//}

- (void)other {
    
    
    NSLog(@"%s", __func__);
}

+ (BOOL)resolveInstanceMethod:(SEL)sel {
    
    
    if (sel == @selector(test)) {
    
    
        // 获取其他方法
        Method method = class_getInstanceMethod(self, @selector(other));

        // 动态添加test方法的实现
        class_addMethod(self,
                        sel,
                        method_getImplementation(method),
                        method_getTypeEncoding(method)
                        );
        // 返回YES代表有动态添加方法
        return YES;
    }
    return [super resolveInstanceMethod:sel];
}

注意:

Method 可以理解为等价于struct method_t *

方法二:

//- (void)test {
    
    
//    NSLog(@"%s",__func__);
//}

void c_other(id self, SEL _cmd) {
    
    
    NSLog(@"c_other - %@ - %@", self, NSStringFromSelector(_cmd));
}

+ (BOOL)resolveInstanceMethod:(SEL)sel {
    
    
    if (sel == @selector(test)) {
    
    
        // 动态添加test方法的实现
        class_addMethod(self,
                        sel,
                        (IMP)c_other,
                        "v16@0:8"
                        );
        // 返回YES代表有动态添加方法
        return YES;
    }
    return [super resolveInstanceMethod:sel];
}

方法实现:

2.2、类方法动态解析

//+ (void)test {
    
    
//    NSLog(@"%s",__func__);
//}
void c_other(id self, SEL _cmd) {
    
    
    NSLog(@"c_other - %@ - %@", self, NSStringFromSelector(_cmd));
}

+ (BOOL)resolveClassMethod:(SEL)sel {
    
    
    if (sel == @selector(test)) {
    
    
        // 注意:第一个参数是类对象【object_getClass(self)】
        class_addMethod(object_getClass(self), sel, (IMP)c_other, "v16@0:8");
        return YES;
    }
    return [super resolveClassMethod:sel];
}

3、消息转发

底层实现代码:

imp = (IMP)_objc_msgforward_impcache;

在这里插入图片描述

开发者可以在forwardInvocation:方法中自定义任何逻辑
以上方法都有对象方法、类方法2个版本(前面可以是加号+,也可以是减号-)

解决方案:

3.1、实例方法

1、将消息转发给另一个实例对象实现

本质:在另外一个实例对象,实现一个一样的方法

- (id)forwardingTargetForSelector:(SEL)aSelector {
    
    
    if (aSelector == @selector(test)) {
    
    
        // objc_msgSend([[Cat alloc] init], aSelector)
        return [[Cat alloc] init];
    }
    return [super forwardingTargetForSelector:aSelector];
}

Cat.h:

#import <Foundation/Foundation.h>
@interface Cat : NSObject
- (void)test;
@end

Cat.m:

#import "Cat.h"

@implementation Cat
- (void)test
{
    
    
    NSLog(@"%s", __func__);
}
@end

2、方法签名

// 方法签名:返回值类型、参数类型
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    
    
    if (aSelector == @selector(test)) {
    
    
        return [NSMethodSignature signatureWithObjCTypes:"v16@0:8"];
        // return [[[Cat alloc] init] methodSignatureForSelector:aSelector];
    }
    return [super methodSignatureForSelector:aSelector];
}

/**
 * NSInvocation封装了一个方法调用,包括:方法调用者、方法名、方法参数
 * anInvocation.target 方法调用者
 * anInvocation.selector 方法名
 * [anInvocation getArgument:NULL atIndex:0]
 */
- (void)forwardInvocation:(NSInvocation *)anInvocation {
    
    
    // 参数顺序:receiver、selector、other arguments
    
    // anInvocation.target == [[MJCat alloc] init]
    // anInvocation.selector == test:
    // anInvocation的参数:15
    // [[[Cat alloc] init] test:15]
    [anInvocation invokeWithTarget:[[Cat alloc] init]];
    
    // 返回值
    int ret;
    [anInvocation getReturnValue:&ret];
    
    NSLog(@"%d", ret);
}

3.2、类方法

1、将消息转发给另一个实例对象实现

+ (id)forwardingTargetForSelector:(SEL)aSelector {
    
    
    // objc_msgSend([[Cat alloc] init], @selector(test))
    // [[[Cat alloc] init] test]
    if (aSelector == @selector(test)) {
    
    
        return [[Cat alloc] init];
    }
    return [super forwardingTargetForSelector:aSelector];
}

2、方法签名

+ (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    
    
    if (aSelector == @selector(test)) {
    
    
        return [NSMethodSignature signatureWithObjCTypes:"v@:"];
    }
    return [super methodSignatureForSelector:aSelector];
}

+ (void)forwardInvocation:(NSInvocation *)anInvocation {
    
    
    NSLog(@"1123");
}

当以上三个阶段没有实现,那就是 objc_msgSend 找不到合适的方法进行调用,会报错unrecognized selector sent to instance

以上是从消息发送后,消息处理的全部流程。

猜你喜欢

转载自blog.csdn.net/weixin_38633659/article/details/124854080
今日推荐