问题 消息机制的三个阶段
1.消息机制发送消息阶段-消息发送
2.消息机制动态方法解析阶段 -动态解析
3.消息机制消息转发阶段- 消息转发
4.线上项目中解决找不到方法奔溃
解决方案
1.消息机制发送消息阶段-消息发送
常用对象方法:
实例对象通过isa 指针 和 某一个值 发生一个位运算 得到该实例的类对象 ,类对象的两个方法列表中(cache_t,method_list_t )寻找对应的方法 如果没有找到,之后通过类对象的superClass 指针找到 父类的类对象中(cache_t,method_list_t)寻找,直到找到root类的类对象为止,这就是消息发送
常用类方法:
类对象通过isa 指针 和 某一个值 发生一个位运算 得到元类对象 ,元类的两个方法列表中(cache_t,method_list_t )寻找对应的方法 如果没有找到,之后通过元类对象的superClass 指针找到 父类的类对象中(cache_t,method_list_t)寻找,直到找到root元类的类对象为止,这就是消息发送
2.消息机制动态方法解析阶段 -动态解析
动态解析触发机制:
消息发送没有 找到对应的方法,就会出发消息的动态解析
a.动态解析C语言函数
void c_other(id self, SEL _cmd)
{
NSLog(@"c_other - %@ - %@", self, NSStringFromSelector(_cmd));
}
+ (BOOL)resolveClassMethod:(SEL)sel
{
if (sel == @selector(test)) {
// 第一个参数是object_getClass(self)
class_addMethod(object_getClass(self), sel, (IMP)c_other, "v16@0:8");
return YES;
}
return [super resolveClassMethod:sel];
}
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
if (sel == @selector(test)) {
// 动态添加test方法的实现
class_addMethod(self, sel, (IMP)c_other, "v16@0:8");
// 返回YES代表有动态添加方法
return YES;
}
return [super resolveInstanceMethod:sel];
}
b.动态解析OC函数
- (void)other
{
NSLog(@"%s", __func__);
}
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
if (sel == @selector(test)) {
// 获取其他方法
Method method = class_getInstanceMethod(self, @selector(other));
// 动态添加test方法的实现
class_addMethod(self, sel,
method_getImplementation(method),
method_getTypeEncoding(method));
// 返回YES代表有动态添加方法
return YES;
}
return [super resolveInstanceMethod:sel];
}
3.消息机制消息转发阶段- 消息转发
消息转发触发机制:
动态解析 resolveInstanceMethod方法为 NO
a.消息转发函数
- (id)forwardingTargetForSelector:(SEL)aSelector
{
if (aSelector == @selector(test)) {
// objc_msgSend([[MJCat alloc] init], aSelector)
return [[MJCat alloc] init];
}
return [super forwardingTargetForSelector:aSelector];
}
b.如果消息转发函数也返回nil,现在就是方法签名
方法签名:返回值类型、参数类型
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
if (aSelector == @selector(test)) {
return [NSMethodSignature signatureWithObjCTypes:"v16@0:8"];
}
return [super methodSignatureForSelector:aSelector];
}
// NSInvocation封装了一个方法调用,包括:方法调用者、方法名、方法参数
// anInvocation.target 方法调用者
// anInvocation.selector 方法名
// [anInvocation getArgument:NULL atIndex:0]
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
// anInvocation.target = [[MJCat alloc] init];
// [anInvocation invoke];
[anInvocation invokeWithTarget:[[MJCat alloc] init]];
}
4.线上项目中解决找不到方法奔溃
@implementation NSBaseObject (bugSolve)
- (void)run
{
NSLog(@"run-123");
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
// 本来能调用的方法
if ([self respondsToSelector:aSelector]) {
return [super methodSignatureForSelector:aSelector];
}
// 找不到的方法
return [NSMethodSignature signatureWithObjCTypes:"v@:"];
}
// 找不到的方法,都会来到这里
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
NSLog(@"找不到%@方法", NSStringFromSelector(anInvocation.selector));
}
@end
网友评论