本章主要探索 动态方法决议和消息转发
在方法没有找到的情况下,其实并不是直接就崩溃,苹果大大给我了我们3次复活的机会
- 动态方法决议
- 快速转发
- 慢速转发
动态方法决议
触发时机:lookUpImpOrForward
慢速查找没有找到时
if (slowpath(behavior & LOOKUP_RESOLVER)) {
behavior ^= LOOKUP_RESOLVER;
return resolveMethod_locked(inst, sel, cls, behavior);
}
static NEVER_INLINE IMP
resolveMethod_locked(id inst, SEL sel, Class cls, int behavior)
{
runtimeLock.assertLocked();
ASSERT(cls->isRealized());
runtimeLock.unlock();
//对象 -- 类
if (! cls->isMetaClass()) { //类不是元类,调用对象的解析方法
// try [cls resolveInstanceMethod:sel]
resolveInstanceMethod(inst, sel, cls);
}
else {//如果是元类,调用类的解析方法, 类 -- 元类
// try [nonMetaClass resolveClassMethod:sel]
// and [cls resolveInstanceMethod:sel]
resolveClassMethod(inst, sel, cls);
//为什么要有这行代码? -- 类方法在元类中是对象方法,所以还是需要查询元类中对象方法的动态方法决议
if (!lookUpImpOrNil(inst, sel, cls)) { //如果没有找到或者为空,在元类的对象方法解析方法中查找
resolveInstanceMethod(inst, sel, cls);
}
}
// chances are that calling the resolver have populated the cache
// so attempt using it
//如果方法解析中将其实现指向其他方法,则继续走方法查找流程
return lookUpImpOrForward(inst, sel, cls, behavior | LOOKUP_CACHE);
}
对象方法的动态决议方法 resolveInstanceMethod
类方法动态决议方法 resolveClassMethod
验证一下,将之前实现的方法注释掉
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person * person = [Person alloc];
[person objcMethod]; //将实现注释掉
[Person classMethod]; //将实现注释掉
}
return 0;
}
#import "Person.h"
#import "objc-runtime.h"
@interface Person()
{
NSString * _cybl; //成员变量
}
@property(nonatomic,copy)NSString * name;
@end
@implementation Person
////类方法
//+(void)classMethod{
//
//}
//
//对象方法
//-(void)objcMethod{
//
//}
- (void)sayMaster {
NSLog(@"sayMaster");
}
+ (void)sayClassMaster {
NSLog(@"sayClassMaster");
}
+ (BOOL)resolveInstanceMethod:(SEL)sel {
if (sel == @selector(objcMethod)){
NSLog(@"%@ 来了", NSStringFromSelector(sel));
//获取sayMaster方法的imp
IMP imp = class_getMethodImplementation(self, @selector(sayMaster));
//获取sayMaster的实例方法
Method sayMethod = class_getInstanceMethod(self, @selector(sayMaster));
//获取sayMaster的丰富签名
const char *type = method_getTypeEncoding(sayMethod);
//将sel的实现指向sayMaster
return class_addMethod(self, sel, imp, type);
}
return [super resolveInstanceMethod:sel];
}
+ (BOOL)resolveClassMethod:(SEL)sel {
if (sel == @selector(classMethod)) {
NSLog(@"%@ 来了", NSStringFromSelector(sel));
IMP imp = class_getMethodImplementation(objc_getMetaClass("Person"), @selector(sayClassMaster));
Method classMethod = class_getInstanceMethod(objc_getMetaClass("Person"), @selector(sayClassMaster));
const char *type = method_getTypeEncoding(classMethod);
return class_addMethod(objc_getMetaClass("Person"), sel, imp, type);
}
return [super resolveClassMethod:sel];
}
@end
运行结果
根据isa走势图
我们可以得到最后都会来到NSObject
的对象方法中查找,我们可以给NSObject
写个分类,然后统一处理,防止崩溃。但是万一系统
#import "NSObject+ResolveCategory.h"
#import "objc-runtime.h"
@implementation NSObject (ResolveCategory)
- (void)sayMaster {
NSLog(@"sayMaster");
}
+ (void)sayClassMaster {
NSLog(@"sayClassMaster");
}
+ (BOOL)resolveInstanceMethod:(SEL)sel {
if (sel == @selector(objcMethod)){
NSLog(@"%@ 来了", NSStringFromSelector(sel));
//获取sayMaster方法的imp
IMP imp = class_getMethodImplementation(self, @selector(sayMaster));
//获取sayMaster的实例方法
Method sayMethod = class_getInstanceMethod(self, @selector(sayMaster));
//获取sayMaster的丰富签名
const char *type = method_getTypeEncoding(sayMethod);
//将sel的实现指向sayMaster
return class_addMethod(self, sel, imp, type);
}
if (sel == @selector(classMethod)) {
NSLog(@"%@ 来了", NSStringFromSelector(sel));
IMP imp = class_getMethodImplementation(objc_getMetaClass("Person"), @selector(sayClassMaster));
Method classMethod = class_getInstanceMethod(objc_getMetaClass("Person"), @selector(sayClassMaster));
const char *type = method_getTypeEncoding(classMethod);
return class_addMethod(objc_getMetaClass("Person"), sel, imp, type);
}else{
NSLog(@"系统的方法 %@ 来了", NSStringFromSelector(sel));
}
return false;
}
@end
输入日志
2020-12-14 14:11:22.366190+0800 Example[44356:242373] 系统的方法 _dynamicContextEvaluation:patternString: 来了
2020-12-14 14:11:22.366835+0800 Example[44356:242373] 系统的方法 descriptionWithLocale: 来了
2020-12-14 14:11:22.366994+0800 Example[44356:242373] 系统的方法 _dynamicContextEvaluation:patternString: 来了
2020-12-14 14:11:22.367117+0800 Example[44356:242373] 系统的方法 _dynamicContextEvaluation:patternString: 来了
2020-12-14 14:11:22.367208+0800 Example[44356:242373] objcMethod 来了
2020-12-14 14:11:22.367289+0800 Example[44356:242373] sayMaster
2020-12-14 14:11:22.367403+0800 Example[44356:242373] 系统的方法 _dynamicContextEvaluation:patternString: 来了
2020-12-14 14:11:22.367506+0800 Example[44356:242373] 系统的方法 descriptionWithLocale: 来了
2020-12-14 14:11:22.367596+0800 Example[44356:242373] classMethod 来了
2020-12-14 14:11:22.367674+0800 Example[44356:242373] sayClassMaster
之前想过,只要走到动态方法决议里,就认为是要崩溃了,直接替换,发现不行,因为底层源码中,有调用resolveInstanceMethod
的时候,也就是说你必须知道sel
的名字
解决方案:我们本质是想替换非系统方法,也就是我们自己写的方法,那问题的根本就需要区分我们自己的方法和系统的方法,如果区分呢,把自己写的所有方法加个前缀比如ym_sayHello
,用前缀来判断,哈哈哈。。。
个人觉得在这一层我其实也干不了啥,再学习学习,待补充吧
快速转发
forwardingTargetForSelector
,将未实现的方法转发给NSObject
,由NSObject
去实现
在 NSObject
的分类中添加如下代码
- (id)forwardingTargetForSelector:(SEL)aSelector {
NSLog(@"%s - %@",__func__,NSStringFromSelector(aSelector));
return [NSObject new];
}
//类方法
+(void)classMethod{
NSLog(@"NSObject -- classMethod");
}
//对象方法
-(void)objcMethod{
NSLog(@"NSObject -- objcMethod");
}
运行日志
2020-12-14 14:38:05.804201+0800 Example[46180:258610] NSObject -- objcMethod
2020-12-14 14:38:05.804822+0800 Example[46180:258610] NSObject -- classMethod
Program ended with exit code: 0
慢速转发
快速转发没找到时,来到最后一次机会
methodSignatureForSelector
forwardInvocation
在Person中实现方法
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
NSLog(@"%s - %@",__func__,NSStringFromSelector(aSelector));
return [NSMethodSignature signatureWithObjCTypes:"v@:"];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation{
NSLog(@"%s - %@",__func__,anInvocation);
}
输入日志
2020-12-14 14:45:24.405470+0800 Example[46687:263923] -[Person methodSignatureForSelector:] - objcMethod
2020-12-14 14:45:24.406279+0800 Example[46687:263923] -[Person forwardInvocation:] - <NSInvocation: 0x101134550>
网友评论