通过前文objc_msgSend底层调用流程探究已经分析到消息发送的几个阶段,接着上文继续向下分析:消息转发
消息转发阶段1
动态方法解析中,如果方法没有实现,则进行消息转发,第一次转发,来到 +/- forwardingTargetForSelector: 方法,注意:实例方法来到-forwardingTargetForSelector:,类方法执行到+forwardingTargetForSelector:;如果forwardingTargetForSelector返回值不为空,找到方法,则程序正常执行,如果返回值为空,则进入消息转发的另外一个阶段;首先看下方示例代码:
@interface Cat : NSObject @end @implementation Cat - (void)test { NSLog(@"%s", __func__); } @end @interface Person : NSObject - (void)test; + (void)test; @end @implementation Person - (id)forwardingTargetForSelector:(SEL)aSelector { return [Cat new]; } + (id)forwardingTargetForSelector:(SEL)aSelector { return [super forwardingTargetForSelector:aSelector]; } @end int main(int argc, const char * argv[]) { @autoreleasepool { Person *person = [Person new]; [person test]; // [Person test]; } return 0; }
打印结果:
-[Cat test]
原因是因为将消息接收者指向了Cat对象,故调用了Cat中的方法,注意类方法的调用需要在+forwardingTargetForSelector:去指定接收对象,如果不指定,则进入消息转发阶段2;
消息转发阶段2
如果上述方法不指定,则进入以下阶段,首先进入 +/- methodSignatureForSelector: 返回方法签名,如果返回为空,则直接崩溃,如果返回不为空,则进入 +/- forwardInvocation: 消息转发阶段(注意此处也要区分实例方法和类方法),看下方示例代码:
@interface Cat : NSObject @end @implementation Cat - (int)test:(int)a { NSLog(@"收到参数: %d", a); return 10; } @end @interface Person : NSObject - (int)test:(int)a; + (void)test; @end @implementation Person - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector { NSLog(@"%s", __func__); if (aSelector == @selector(test:)) { return [NSMethodSignature signatureWithObjCTypes:"i@:i"]; // return [[Cat new] methodSignatureForSelector:aSelector]; } return [super methodSignatureForSelector:aSelector]; } - (void)forwardInvocation:(NSInvocation *)anInvocation { NSLog(@"%s", __func__); int p; [anInvocation getArgument:&p atIndex:2]; NSLog(@"原始参数: %d", p); int a = 100; //此处将传入的参数改为了100 [anInvocation setArgument:&a atIndex:2]; [anInvocation invokeWithTarget:[Cat new]]; int ret; //接收返回值 [anInvocation getReturnValue:&ret]; NSLog(@"返回值: %d", ret); } @end int main(int argc, const char * argv[]) { @autoreleasepool { Person *person = [Person new]; [person test:20]; // [Person test]; } return 0; }
打印结果:
-[Person methodSignatureForSelector:] -[Person forwardInvocation:]
原始参数: 20
收到参数: 100
返回值: 10
应用场景
防止OC对象方法调用产生的崩溃
原理:通过hook NSObject中 +/- forwardingTargetForSelector: 方法,动态的创建类,并添加方法实现,为了不影响主类中原有已经实现了的消息转发流程对应方法,故先判断了原类中是否实现对应的方法,再去决定是否动态创建类并添加方法,完整代码实现请参考以下代码(仅交换了实例方法、类方法自行修改方法交换处即可):
@interface NSObject (Cate) @end @implementation NSObject (Cate) + (void)load { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ [self hookMethod:[NSObject class] originSelector:@selector(forwardingTargetForSelector:) swizzledSelector:@selector(hook_forwardingTargetForSelector:)]; }); } + (void)hookMethod:(Class)cls originSelector:(SEL)originalSelector swizzledSelector:(SEL)swizzledSelector { Method originalMethod = class_getInstanceMethod(cls, originalSelector); Method swizzledMethod = class_getInstanceMethod(cls, swizzledSelector); BOOL didAddMethod = class_addMethod(cls, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod)); if (didAddMethod) { class_replaceMethod(cls, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod)); } else { method_exchangeImplementations(originalMethod, swizzledMethod); } } - (BOOL)isImplementation:(SEL)selector { return class_getInstanceMethod([NSObject class], selector) != class_getInstanceMethod([self class], selector); } - (id)hook_forwardingTargetForSelector:(SEL)selector { if ([self isImplementation:@selector(forwardingTargetForSelector:)]) { return [self hook_forwardingTargetForSelector:selector]; } if ([self isImplementation:@selector(methodSignatureForSelector:)]) { return [self hook_forwardingTargetForSelector:selector]; } if ([self isImplementation:@selector(forwardInvocation:)]) { return [self hook_forwardingTargetForSelector:selector]; } NSLog(@"-[%@ %@] - 方法未实现", NSStringFromClass([self class]), NSStringFromSelector(selector)); NSString *className = @"LTReportClass"; Class registerClass = NSClassFromString(className); IMP imp = imp_implementationWithBlock(^(id _) { }); if (!registerClass) { registerClass = objc_allocateClassPair([NSObject class], className.UTF8String, 0); objc_registerClassPair(registerClass); } class_addMethod(registerClass, selector, imp, nil); return [registerClass new]; } @end @interface Person : NSObject @property(assign, nonatomic) NSInteger age; - (void)test; - (void)personMethod; @end @implementation Person //@synthesize age = _age; //为age生成_age的成员变量,以及getter和setter @dynamic age; // 系统默认不再实现getter和setter方法 - (void)test { NSLog(@"%s - %ld", __func__, self.age); } @end int main(int argc, const char * argv[]) { @autoreleasepool { Person *person = [Person new]; [person test]; person.age = 10; [person personMethod]; } return 0; }
打印结果:
-[Person age] - 方法未实现 -[Person test] - 0 -[Person setAge:] - 方法未实现 -[Person personMethod] - 方法未实现
多代理消息分发实现
多代理的应用场景一般为,比如IM消息,打开了多个页面,然而底层消息接收器只有一个,多个页面中都需要接收到消息,使用多代理进行分发比较方便,另外跨页面传值也可以使用多代理,可以解决页面事件一层一层“成串”传递的问题;
实现原理:使用链表存储代理对象,以及当前代理分发所在的队列,然后利用消息转发,批量的根据代理对象是否可以响应selecotr,来进行协议方法的调用,核心源码实现如下(因篇幅原因,此处仅列出部分代码,完整代码请参考文末链接):
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector { LTDelegateNode *curNode = self.headNode; while (curNode) { if ([curNode.delegate respondsToSelector:aSelector]) { NSMethodSignature *signature = [curNode.delegate methodSignatureForSelector:aSelector]; if (signature) { return signature; } } curNode = curNode.next; } return [super methodSignatureForSelector:@selector(_avoidCrash)]; } - (void)forwardInvocation:(NSInvocation *)anInvocation { LTDelegateNode *curNode = self.headNode; while (curNode) { if ([curNode.delegate respondsToSelector:anInvocation.selector]) { dispatch_async(curNode.queue, ^{ @autoreleasepool { [anInvocation invokeWithTarget:curNode.delegate]; } }); } curNode = curNode.next; } }
调用测试代码:
[[LTElegantAgentManager sharedManager] addDelegate:self delegateQueue:dispatch_get_main_queue()]; [[LTElegantAgentManager sharedManager] removeDelegate:self]; [(id <LTAgentManager>)[LTElegantAgentManager sharedManager] test:123]; [(id <OtherDelegate>)[LTElegantAgentManager sharedManager] otherMethod];
总结
消息转发:
- 第一阶段: +/- forwardingTargetForSelector: 可以指定消息接收者,如果返回nil,则进入下一阶段;
- 第二阶段: +/- methodSignatureForSelector: 如果返回nil,则直接崩溃(unrecognized selector sent to instance),如果返回的有方法签名,则执行 +/- forwardInvocation:方法
消息转发使用场景:
- 防止崩溃
- 多代理分发
- 利用forwardInvocation:拿到class和selector可以做一些hook工作,比如热更等
@synthesize与@dynamic修饰词的作用?
- @dynamic系统默认不再实现getter和setter方法
- @synthesize1. 同时重写了属性的getter和setter时,系统就不再会生成ivar,使用 @synthesize age = _age,可以来关联属性和ivar;2. 重写了只读属性的getter时,可以利用@synthesize来进行关联,否则无法访问_变量;3. 可以对@protocol和category中定义的属性,利用@synthesize来进行关联;4. 重载的属性(继承),当在子类中继承了父类的属性,必须使用@synthesize来手动合成ivar;
源码地址:https://github.com/gltwy/public
本文部分内容来可能来源于网络,发布的内容如果侵犯了您的权益,请联系我们尽快删除!