注册
iOS

objc_msgsend(中)方法动态决议


引入

在学习本文之前我们应该了解

当快速消息查找和消息慢速查找都也找不到imp时,苹果系统后续是怎么处理的我们一起来学习! 方法动态决议主要做了哪些事情?

准备工作

resolveMethod_locked动态方法决议

1.png

  • 赋值imp = forward_imp

  • 做了个单例判断动态控制执行流程根据behavior方法只执行一次。

2.png

对象方法的动态决议

3.png

类方法的动态决议

3.png

lookUpImpOrForwardTryCache

4.png

cache_getImp

5.png

  • 苹果给与一次动态方法决议的机会来挽救APP
  • 如果是类请用resolveInstanceMethod
  • 如果是元类请用resolveClassMethod

如果都没有处理那么imp = forward_imp ,const IMP forward_imp = (IMP)_objc_msgForward_impcache;

_objc_msgForward_impcache探究

6.png

  • __objc_forward_handler主要看这个函数处理

__objc_forward_handler

7.png

代码案例分析

   int main(int argc, const char * argv[]) {
    @autoreleasepool {
        LGTeacher *p = [LGTeacher alloc];
        [t sayHappy];
[LGTeacher saygood];
    }
    return 0;
}


崩溃信息

2021-11-28 22:36:39.223567+0800 KCObjcBuild[12626:762145] +[LGTeacher sayHappy]: unrecognized selector sent to class 0x100008310

2021-11-28 22:36:39.226012+0800 KCObjcBuild[12626:762145] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '+[LGTeacher sayHappy]: unrecognized selector sent to class 0x100008310'

复制代码

动态方法决议处理对象方法找不到

代码动态决议处理imp修复崩溃

@implementation LGTeacher

-(void)text{
    NSLog(@"%s", __func__ );
}

+(void)say777{

    NSLog(@"%s", __func__ );
}

// 对象方法动态决议

+(BOOL**)resolveInstanceMethod:(SEL)sel{

    if (sel == @selector(sayHappy)) {

        IMP imp =class_getMethodImplementation(self, @selector(text));
        Method m = class_getInstanceMethod(self, @selector(text));
        const char * type = method_getTypeEncoding(m);
        return** class_addMethod(self, sel, imp, type);
    }
    return [super resolveInstanceMethod:sel];

}

//类方法动态决议

+ (BOOL)resolveClassMethod:(SEL)sel{
    if (sel == @selector(saygood)) {
        IMP  imp7 = class_getMethodImplementation(objc_getMetaClass("LGTeacher"), @selector(say777));
        Method m  = class_getInstanceMethod(objc_getMetaClass("LGTeacher"), @selector(say777));
        const char type = method_getTypeEncoding(m);
        return class_addMethod(objc_getMetaClass("LGTeacher"), sel, imp7, type);
    }

    return [super resolveClassMethod:sel];

}

@end


运行打印信息

2021-11-29 16:30:46.403671+0800 KCObjcBuild[27071:213498] -[LGTeacher text]

2021-11-29 16:30:46.404186+0800 KCObjcBuild[27071:213498] +[LGTeacher say777]

  • 找不到imp我们动态添加一个imp ,但这样处理太麻烦了。
  • 实例方法方法查找流程 类->父类->NSObject->nil
  • 类方法查找流程 元类->父类->根元类-NsObject->nil

最终都会找到NSobject.我们可以在NSObject统一处理 所以我们可以给NSObject创建个分类

@implementation NSObject (Xu)
+(BOOL)resolveInstanceMethod:(SEL)sel{
if (@selector(sayHello) == sel) {
NSLog(@"--进入%@--",NSStringFromSelector(sel));
IMP imp = class_getMethodImplementation(self , @selector(sayHello2));
Method meth = class_getInstanceMethod(self , @selector(sayHello2));
const char * type = method_getTypeEncoding(meth);
return class_addMethod(self ,sel, imp, type);;

}else if (@selector(test) == sel){
NSLog(@"--进入%@--",NSStringFromSelector(sel));
IMP imp = class_getMethodImplementation(object_getClass([self class]), @selector(newTest));
Method meth = class_getClassMethod(object_getClass([self class]) , @selector(newTest));
const char * type = method_getTypeEncoding(meth);
return class_addMethod(object_getClass([self class]) ,sel, imp, type);;
}
return NO;
}

- (void)sayHello2{
NSLog(@"--%s---",__func__);
}

+(void)newTest{
NSLog(@"--%s---",__func__);
}

@end


实例方法是类方法调用,系统都自动调用了resolveInstanceMethod方法,和上面探究的吻合。 动态方法决议优点

  • 可以统一处理方法崩溃的问题,出现方法崩溃可以上报服务器,或者跳转到首页
  • 如果项目中是不同的模块你可以根据命名不同,进行业务的区别
  • 这种方式叫切面编程熟成AOP

方法动态决议流程图

9.png

问题

  • resolveInstanceMethod为什么调用两次?
  • 统一处理方案怎么处理判断问题,可能是对象方法崩溃也可能是类方法崩溃,怎么处理?
  • 动态方法决议后苹果后续就没有处理了吗?

链接:https://juejin.cn/post/7035965819955707935

0 个评论

要回复文章请先登录注册