objc_msgSend学习

2020-02-28  本文已影响0人  Tony17

前言

OC的方法调用,其实就是通过objc_msgSend函数的调用。通过对 objc_msgSend 执行过程中的一些处理,我们可以达到防御unrecognized selector sent to instance问题,也可以像著名的开源库 Aspect那样做到在已知方法中插入代码或者直接替换已知方法的实现。

objc_msgSend 流程

objc_msgSend的执行流程分为三大阶段:

  1. 消息发送
  2. 动态方法解析
  3. 消息转发
  4. 报错 unrecognized selector sent to instance
objc_msgSend-消息发送.png
objc_msgSend-动态解析.png
objc_msgSend-消息转发.png

相关代码实现:

+resolveInstanceMethod:或者+resolveClassMethod:方法

+ (BOOL)resolveInstanceMethod:(SEL)sel {
    Method method = class_getInstanceMethod(self, @selector(errorSelector));
    class_addMethod([self class], sel, method_getImplementation(method), method_getTypeEncoding(method));
    return [super resolveInstanceMethod:sel];
}

+ (BOOL)resolveClassMethod:(SEL)sel {
    Method method = class_getInstanceMethod(self, @selector(errorSelector));
    class_addMethod([self class], sel, method_getImplementation(method), method_getTypeEncoding(method));
    return [super resolveInstanceMethod:sel];
}

forwardingTargetForSelector:方法, 该方法分为实例方法和类方法,对应不同的方法调用。返回值也要区分是类对象还是实例对象。

- (id)forwardingTargetForSelector:(SEL)aSelector {
    return [ErrorSelector new];
}

+ (id)forwardingTargetForSelector:(SEL)aSelector {
    return [ErrorSelector class];
}

methodSignatureForSelector:forwardInvocation:方法, 该方法分为实例方法和类方法,对应不同的方法调用。方法内部的处理可以是一样的,不用区分类和实例的区别。

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    // 方法签名就是方法的返回值类型,参数类型
    NSMethodSignature *sign = [NSMethodSignature signatureWithObjCTypes:"v@:"];
    return sign;
}
- (void)forwardInvocation:(NSInvocation *)anInvocation {
    // NSInvocation 封装了一个方法调用,包括:方法调用者,方法名,参数
    [anInvocation invokeWithTarget:[[ErrorSelector alloc]init]];
}
+ (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    // 方法签名就是方法的返回值类型,参数类型
//    NSMethodSignature *sign = [NSMethodSignature signatureWithObjCTypes:"v@:"];
    NSMethodSignature *sign = [[ErrorSelector new] methodSignatureForSelector:@selector(errorSelector)];
    return sign;
}
+ (void)forwardInvocation:(NSInvocation *)anInvocation {
    // NSInvocation 封装了一个方法调用,包括:方法调用者,方法名,参数
    [anInvocation invokeWithTarget:[[ErrorSelector alloc]init]];
}

最后

以上就是本篇的内容,势必会有遗漏的地方,欢迎斧正~

上一篇下一篇

猜你喜欢

热点阅读