objc_msgSend学习
2020-02-28 本文已影响0人
Tony17
前言
OC
的方法调用,其实就是通过objc_msgSend
函数的调用。通过对 objc_msgSend 执行过程中的一些处理,我们可以达到防御unrecognized selector sent to instance
问题,也可以像著名的开源库 Aspect
那样做到在已知方法中插入代码或者直接替换已知方法的实现。
objc_msgSend 流程
objc_msgSend
的执行流程分为三大阶段:
- 消息发送
- 动态方法解析
- 消息转发
- 报错
unrecognized selector sent to instance
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]];
}
最后
以上就是本篇的内容,势必会有遗漏的地方,欢迎斧正~