author | category | labels | discussion | updatedAt |
---|---|---|---|---|
Zhang Peng |
🙌 Show and tell |
iOS |
2024-04-08 01:04:59 +0800 |
当对象接收到无法解读的消息(unrecognized selector sent to instance 0x87
),就会启动消息转发机制,由程序员指定处理方法。
-
动态方法解析
检查当前接收者能否动态添加方法,处理这个
unrecognized selector
。 -
完整的消息转发
第一阶段执行完毕后,如接收者不能以动态新增方法处理这个
unrecognized selector
,接下来,会分两种情况:-
有备援接收者(replacement receiver)
在运行期将消息转给备援接收者,由备援接收者完成消息的处理。
-
无备援接收者(replacement receiver)
-
启动一套“完整的消息转发机制”,将消息封装到 NSInvocation 对象中,交给接收者处理。
-
如果没有找到需要执行的方法,会根据方法的类型,执行不同的处理方法。
处理无法调用的类方法
+ (BOOL)resolveClassMethod:(SEL)sel;
处理无法调用的实例方法
+ (BOOL)resolveInstanceMethod:(SEL)sel;
一般是提前写好相关的实现代码,通过 Runtime 在此处插入到类中。
e.g.
/**
没有找到 SEL 的实现时会执行下方的方法
@param sel 当前对象调用并且找不到 IML 的 SEL
@return 是否可以处理这个方法,并返回 yes
*/
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
// 当返回 YES 时
// 在这里通过 Runtime 在将已经写好实现的代码插入到类中。
// ...
return YES;
// 当返回 NO 时
// 会接着执行 forwordingTargetForSelector:方法
// return NO;
}
在方法 - (id)forwardingTargetForSelector:(SEL)aSelector
中返回可以处理该消息的对象,交由该对象去处理这个消息。
如果此处返回 nil,则表示没有其他对象可以处理这个消息,然后通过完整的消息转发机制来处理。
/**
将当前对象不存在的 SEL 传给其他存在该 SEL 的对象
@param aSelector 当前类中不存在的 SEL
@return 存在该 SEL 的对象
*/
- (id)forwardingTargetForSelector:(SEL)aSelector
{
// 不传递给其他对象
// 将会执行 - (void)forwardInvocation:(NSInvocation *)anInvocation;
return nil;
// 传递给一个其他对象,处理这个方法。
// 可以做相应的错误处理等
// 让 OtherClass 中相应的 SEL 去执行该方法
// return [[OtherClass alloc] init];
}
如果接收者不能处理消息,并且没有备援接收者,最终只能采取一个完整的消息转发来处理消息。
通过 NSInvocation 包装方法的目标、参数等,然后通过 - (void)forwardInvocation:(NSInvocation *)invocation
将消息指派给目标对象。
- (void)forwardInvocation:(NSInvocation *)invocation
{
SecondClass * forwardClass = [SecondClass new];
SEL sel = invocation.selector;
if ([forwardClass respondsToSelector:sel]) {
[invocation invokeWithTarget:forwardClass];
} else {
[self doesNotRecognizeSelector:sel];
}
}