iOS 消息传递

非 Y 不嫁゛ 提交于 2020-03-05 21:13:18

Objective-C 调用方法实际上是发送消息的过程,先判断接收者是否为空,如果不为空经历以下几步:
1、先从方法的缓存中查找,如果没找到进入下一步。
2、通过对象的isa指针找到类对象,在类对象的方法列表中查找;如果没找到就通过superclass指针逐层查找直到NSObject对象。如果还是没找到就执行方法动态解析。
3、方法动态解析调用:+ (BOOL)resolveInstanceMethod:(SEL)sel 方法来为实例方法sel动态添加实现方法。如果没添加进入下一步。
4、重定向消息的接收者,通过 - (id)forwardingTargetForSelector:(SEL)aSelector 来返回可处理这条消息的对象。如果这个对象不响应这个消息则进入消息转发。
5、消息转发调用 - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector- (void)forwardInvocation:(NSInvocation *)anInvocation

如果通过以上过程还没有处理这条消息就会发生crash(unrecognized selector sent to instance xxxx),
接下来一步步实现方法动态解析、重定向消息的接收者、消息转发

方法动态解析

首先我们发送一个未定义的消息:[self performSelector:@selector(message)]; 让它进入方法解析,在方法解析中添加动态方法时需要导入#import <objc/runtime.h>

- (void)viewDidLoad {
    [super viewDidLoad];
    
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wundeclared-selector"
    [self performSelector:@selector(message)];
#pragma clang diagnostic pop
}

void dynamicMethodIMP(id self, SEL _cmd) {
    NSLog(@"%@ selector:%@", self, NSStringFromSelector(_cmd));
    UIViewController *vc = (UIViewController *)self;
    vc.view.backgroundColor = [UIColor lightGrayColor];
}

// 方法解析
+ (BOOL)resolveInstanceMethod:(SEL)sel {
    NSString *crashStr = NSStringFromSelector(sel);
    if ([crashStr isEqualToString:@"message"]) {
        class_addMethod([self class], sel, (IMP)dynamicMethodIMP, "v@:");
        return YES;
    }
    return [super resolveInstanceMethod:sel];
}

结果:

 Demo[6280:817719] <ViewController: 0x7f9298c03ae0> selector:message

重定向消息的接收者

1、创建一个Person类,并在.m文件中定义一个work方法

# Person.h

#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface Person : NSObject
@end
NS_ASSUME_NONNULL_END


# Person.m
#import "Person.h"

@implementation Person

- (void)work {
    NSLog(@"%@ work", self);
}

@end

2、我们将发送的消息 message 改为 work,动态方法解析失败就调用 - (id)forwardingTargetForSelector:(SEL)aSelector
来为方法分配合适的接收者。

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wundeclared-selector"
    [self performSelector:@selector(work)];
#pragma clang diagnostic pop
// 重定向消息接收者
- (id)forwardingTargetForSelector:(SEL)aSelector {
    Person *person = [[Person alloc] init];
    if ([person respondsToSelector:aSelector]) {// 判断接收者是否响应该方法
        return person;
    }
    return nil;
}

结果:

 Demo[6309:821439] <Person: 0x60000131bbd0> work

消息转发

我们再将发送的消息 work 改为 sendOtherMessage,通过以上两步都没有能处理这条消息的方法就走消息转发流程。

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wundeclared-selector"
    [self performSelector:@selector(sendOtherMessage)];
#pragma clang diagnostic pop
// 消息转发
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    return [NSMethodSignature signatureWithObjCTypes:"v@:"];
}

- (void)forwardInvocation:(NSInvocation *)anInvocation {
    [anInvocation setSelector:@selector(forwardMethod)];
    [anInvocation invokeWithTarget:self];
}

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

结果:

 Demo[6325:825256] -[ViewController forwardMethod]
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!