2020-4-17
_objc_msgForward
用于消息转发:向一个对象发送消息,但是它没有实现的时候,_objc_msgForward
会尝试走消息转发。
_objc_msgForward
和 _objc_msgForward_stret
区别,引用自: JSPatch 实现原理详解
对于某些架构某些 struct,必须使用 _objc_msgForward_stret 代替 _objc_msgForward。为什么要用 _objc_msgForward_stret 呢,找到一篇说明 objc_msgSend_stret 和 objc_msgSend 区别的文章),说得比较清楚,原理是一样的,是C的一些底层机制的原因,简单复述一下:
大多数 CPU 在执行 C 函数时会把前几个参数放进寄存器里,对
obj_msgSend
来说前两个参数固定是self
/_cmd
,它们会放在寄存器上,在最后执行完后返回值也会保存在寄存器上,取这个寄存器的值就是返回值。
调用 resolveInstanceMethod:
方法 (或 resolveClassMethod:
)。允许用户在此时为该 Class 动态添加实现。如果有实现了,则调用并返回YES,那么重新开始 objc_msgSend
流程。如果仍没实现,继续转发。
代码实例:
为 Test
动态添加类方法class_print
和 实例方法instance_print
void instance_print(id self, SEL _cmd, NSString *text)
{
NSLog(@"replaced instance method %@", text);
}
void class_print(id self, SEL _cmd, NSString *text)
{
NSLog(@"replaced resolve class method %@", text);
}
@implementation Test
+ (BOOL)resolveClassMethod:(SEL)sel {
if (sel == @selector(classPrint:)) {
// 类方法列表在元类中查找
class_addMethod(object_getClass(self), sel, (IMP)class_print, "v@:@");
return YES;
}
return [super resolveClassMethod:sel];
}
+ (BOOL)resolveInstanceMethod:(SEL)sel {
if (sel == @selector(instancePrint:)) {
class_addMethod([self class], sel, (IMP)instance_print, "v@:@");
return YES;
}
return [super resolveInstanceMethod:sel];
}
@end
调用 forwardingTargetForSelector:
方法,找到一个能响应该消息的对象。如果获取到,则直接把消息转发给它,否则返回 nil ,继续转发。
代码示例:
把Test
的实例方法run
转发给Person
对象去执行。
@implementation Test
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
if (aSelector == @selector(read)) {
return [NSMethodSignature signatureWithObjCTypes:"v@:"];
} else {
return [super methodSignatureForSelector:aSelector];
}
}
@end
@implementation Person
- (void)run {
NSLog(@"%@ run", [self class]);
}
@end
- 调用
methodSignatureForSelector:
方法,尝试获得一个方法签名。如果获取不到,则直接调用doesNotRecognizeSelector
抛出异常。如果能够获取,继续进行第2步: - 调用
forwardInvocation:
方法,将第 1 步获取到的方法签名包装成NSInvocation
对象传入,处理消息转发。
示例代码:
把Test
的实例方法read
转发给Student
对象去执行。
@implementation Test
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
if (aSelector == @selector(read)) {
return [NSMethodSignature signatureWithObjCTypes:"v@:"];
} else {
return [super methodSignatureForSelector:aSelector];
}
}
- (void)forwardInvocation:(NSInvocation *)anInvocation {
if (anInvocation.selector == @selector(read)) {
Student *s = [Student new];
[anInvocation invokeWithTarget:s];
} else {
[super forwardInvocation:anInvocation];
}
}
@end
@implementation Student
- (void)read {
NSLog(@"%@ read", [self class]);
}
@end