小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
我们先看下方法动态决议中,类对象的resolveClassMethod
方法。
static void resolveClassMethod(id inst, SEL sel, Class cls) {
runtimeLock.assertUnlocked();
ASSERT(cls->isRealized());
ASSERT(cls->isMetaClass());
if (!lookUpImpOrNilTryCache(inst, @selector(resolveClassMethod:), cls)) {
return;
}
Class nonmeta;
{
mutex_locker_t lock(runtimeLock);
nonmeta = getMaybeUnrealizedNonMetaClass(cls, inst);
if (!nonmeta->isRealized()) {
_objc_fatal("nonmeta class %s (%p) unexpectedly not realized", nonmeta->nameForLogging(), nonmeta);
}
}
BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
bool resolved = msg(nonmeta, @selector(resolveClassMethod:), sel);
IMP imp = lookUpImpOrNilTryCache(inst, sel, cls);
}
- 入参:
inst
:类对象sel
:找不到的类方法cls
:元类
- 调用
lookUpImpOrNilTryCache
函数,内部调用_lookUpImpTryCache
函数,对当前类对象的resolveClassMethod
方法进行消息慢速查找- 在NSObject中,该方法已默认实现
- 继承自NSObject的类对象,不会被return拦截
- 调用
getMaybeUnrealizedNonMetalClass
函数,验证当前类对象和元类的关系,返回一个普通类 - 系统使用
objc_msgSend
,发送resolveClassMethod
消息- 消息接收者为类对象
- 消息主体中的
SEL
为resolveClassMethod
- 参数为找不到的类方法
- 调用
lookUpImpOrNilTryCache
函数,对之前找不到的类方法进行消息慢速查找- 如果在
resolveInstanceMethod
成功处理,返回处理后的imp - 如果找依然找不到方法,返回
_objc_msgForward_impcache
函数地址,进入消息转发流程 找到getMaybeUnrealizedNonMetaClass
函数的定义
- 如果在
static Class getMaybeUnrealizedNonMetaClass(Class metacls, id inst) {
static int total, named, secondary, sharedcache, dyld3;
runtimeLock.assertLocked();
ASSERT(metacls->isRealized());
total++;
if (!metacls->isMetaClass()) return metacls;
if (metacls->ISA() == metacls) {
Class cls = metacls->getSuperclass();
ASSERT(cls->isRealized());
ASSERT(!cls->isMetaClass());
ASSERT(cls->ISA() == metacls);
if (cls->ISA() == metacls) return cls;
}
if (inst) {
Class cls = remapClass((Class)inst);
while (cls) {
if (cls->ISA() == metacls) {
ASSERT(!cls->isMetaClassMaybeUnrealized());
return cls;
}
cls = cls->getSuperclass();
}
#if DEBUG
_objc_fatal("cls is not an instance of metacls");
#else
// release build: be forgiving and fall through to slow lookups
#endif
}
...
}
- 判断cls,如果非元类,直接返回
- 如果cls为元类,且isa指向自己,证明当前cls为根元类,获取其父类NSObject并返回
- 遍历当前类及其父类,找到isa指向元类的所属类
- 如果均为找到,
DEBUG
模式下,错误提示:当前类对象不是该元类的实例 Release
模式下,按以下流程查找该元类的类对象- 查看元类是否存在指向其非元类的指针,存在直接返回
- 按照元类的
mangledName
查找类对象,如果存在且isa指向元类,将其返回 - 在全局Map中查找类对象,存在将其返回
- 在dyld的
closure table
中查找类对象,存在将其返回 - 在共享缓存中查找类对象,存在将其返回
- 以上流程均未找到,错误提示:没有指向该元类的类
案例
在LGPerson.h中,声明
sayNB
类方法
#import <Foundation/Foundation.h>
@interface LGPerson : NSObject
+(void)sayNB;
@end
在LGPerson.m中,实现say666
实例方法和resolveInstanceMethod
类方法,未实现sayNB
实例方法
#import "LGPerson.h"
#import <objc/runtime.h>
@implementation LGPerson
-(void)say666{
NSLog(@"实例方法-say666");
}
+ (BOOL)resolveClassMethod:(SEL)sel{
if(sel==@selector(sayNB)){
NSLog(@"resolveClassMethod:%@,%@", self, NSStringFromSelector(sel));
IMP imp = class_getMethodImplementation(self, @selector(say666));
Method methodSay666 = class_getInstanceMethod(self, @selector(say666));
const char *type = method_getTypeEncoding(methodSay666);
const char * c = NSStringFromClass(self).UTF8String;
return class_addMethod(objc_getMetaClass(c), @selector(sayNB), imp, type);
}
return [super resolveClassMethod:sel];
}
@end
- 如果调用的类方法为
sayNB
,动态添加sayNB
方法,并将imp填充为say666
的函数地址 - 由于需要添加的
sayNB
是类方法,所以需要在元类中添加
在main函数中,调用LGPerson的sayNB类方法
int main(int argc, const char * argv[]) {
@autoreleasepool {
[LGPerson sayNB];
}
return 0;
}
-------------------------
//输出结果:
实例方法-say666
- 自动进入
resolveClassMethod
方法