消息转发

996的生活持续了好久,期间项目组各种新项目,博客感觉要荒废,那把这段时间项目期间的大神分享拿来总结一下 ^_^

之前对于消息转发一直处于懵懂状态,因为平时用到的并不多,通过这个分享详细研究下~~~

参考文章ibireme


oc中调用方法被称为发消息,消息转发就是当找不到接收者时会对消息转发给能响应的对象,转发的核心方法就是_objc_msgForward

正常情况下,我们运行时调用一个没有实现的方法会崩溃,我们通过

暂停程序运行,并在lldb中输入下面的命令:
call (void)instrumentObjcMessageSends(YES)
/tmp/msgSend-xxx文件可以看到运行时发送的信息

resolveInstanceMethod提供动态添加方法支持

1
2
3
4
5
6
7
8
+ (BOOL)resolveInstanceMethod:(SEL)sel {
BOOL isResolved = [super resolveInstanceMethod:sel];
if (!isResolved) {
class_addMethod(<#Class _Nullable __unsafe_unretained cls#>, <#SEL _Nonnull name#>, <#IMP _Nonnull imp#>, <#const char * _Nullable types#>)
return YES;
}
return isResolved;
}

- (id)forwardingTargetForSelector:(SEL)aSelector 转发消息返回响应者

1
2
3
4
5
6
- (id)forwardingTargetForSelector:(SEL)aSelector {
if (aSelector == @selector(forward)) {
return [[XXX alloc] init];
}
return [super forwardingTargetForSelector:aSelector];
}

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector返回方法签名

1
2
3
4
5
6
7
8
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
NSMethodSignature *methodSignature = [super methodSignatureForSelector:aSelector];
if (!methodSignature) {
//此处消息转发调用
return [self methodSignatureForSelector:@selector(forwardResponse)];
}
return methodSignature;
}

如果上面的方法返回的方法签名为空,调用

doesNotRecognizeSelector调用该方法后,系统会抛出异常

如果方法签名不为空,就会调用
- (void)forwardInvocation:(NSInvocation *)anInvocation

1
2
3
4
5
6
7
8
- (void)forwardInvocation:(NSInvocation *)anInvocatio{
anInvocatio.selector = @selector(forwardResponse);
[anInvocatio invoke];
}
- (void)forwardResponse {
NSLog(@"%s",__func__);
}

到此为止,消息转发结束;

可以看出在运行时有以下几个核心方法

1
2
3
4
+ (BOOL)resolveInstanceMethod:(SEL)sel OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
- (id)forwardingTargetForSelector:(SEL)aSelector OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector OBJC_SWIFT_UNAVAILABLE("");
- (void)forwardInvocation:(NSInvocation *)anInvocation;

其中

1
2
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector OBJC_SWIFT_UNAVAILABLE("");`
- (void)forwardInvocation:(NSInvocation *)anInvocation;

两个是消息转发的核心方法,我们要保证返回的NSMethodSignature不能为空,然后在forwardInvocation中配置目标方法


下面举一个实例:

比如说我们正常开发项目实现了一个通用页面,我们只需要保证传进去的数据源符合HHTestMsgForwardView这个protocol就能正常使用,

但突然有一天,产品经理给一个类似页面添加一个特殊效果滚动时做一些特别的事情

我们首先要实现tableviewUIScrollViewDelegate中的方法,此时如果将这些方法放到通用页面中实现,那么别的地方使用的时候是没必要的,我们可以通过_objc_msgForward将这一部分转发出来给适应需求的地方实现

可以看出scrollView的代理事件已经被转发出来了.

戳这里看源码,以上只是简单的使用,后续还要更加深入研究。


学习计划:
1、开源库中的使用 YYWeakProxyIGListKitAspectsJSPatch
2、扩展实现一对多双向通信XMPPFrameworkGCDMulticastDelegate

学习需要深入参考的文章

Objective-C 消息发送与转发机制原理

JSPatch 实现原理详解

AOP. Delivered.

面向切面编程之 Aspects 源码解析及应用