前言:
上小节,我们讲到了从 cache缓存
中查找消息的imp,如果没有找到
的话,继续往下查找,也就是所谓的慢速查找
流程,即从类的方法列表中去查找。
- 断点调试验证:
step1:
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
LGPerson *person = [LGPerson alloc];
[person sayHello]; 1断点
// [person say666];
}
return 0;
}
step2:

step3: step into(未从缓存中找到imp,发送_objc_msgSend_uncached消息)

step4: step into(然后开始进入慢速查找imp流程)

核心代码:
IMP lookUpImpOrForward(id inst, SEL sel, Class cls, int behavior)
{
const IMP forward_imp = (IMP)_objc_msgForward_impcache;
IMP imp = nil;
Class curClass;
runtimeLock.assertUnlocked();
// Optimistic cache lookup
if (fastpath(behavior & LOOKUP_CACHE)) {
imp = cache_getImp(cls, sel);
if (imp) goto done_nolock;
}
runtimeLock.lock();
checkIsKnownClass(cls);
if (slowpath(!cls->isRealized())) {
cls = realizeClassMaybeSwiftAndLeaveLocked(cls, runtimeLock);
// runtimeLock may have been dropped but is now locked again
}
if (slowpath((behavior & LOOKUP_INITIALIZE) && !cls->isInitialized())) {
cls = initializeAndLeaveLocked(cls, inst, runtimeLock);
}
runtimeLock.assertLocked();
curClass = cls;
for (unsigned attempts = unreasonableClassCount();;) {
// curClass method list.
Method meth = getMethodNoSuper_nolock(curClass, sel);
if (meth) {
imp = meth->imp;
goto done;
}
if (slowpath((curClass = curClass->superclass) == nil)) {
// No implementation found, and method resolver didn't help.
imp = forward_imp;
break;
}
// Halt if there is a cycle in the superclass chain.
if (slowpath(--attempts == 0)) {
_objc_fatal("Memory corruption in class list.");
}
// Superclass cache.
imp = cache_getImp(curClass, sel); // 有问题???? cache_getImp - lookup - lookUpImpOrForward
if (slowpath(imp == forward_imp)) {
break;
}
if (fastpath(imp)) {
// Found the method in a superclass. Cache it in this class.
goto done;
}
}
// No implementation found. Try method resolver once.
if (slowpath(behavior & LOOKUP_RESOLVER)) {
behavior ^= LOOKUP_RESOLVER;
return resolveMethod_locked(inst, sel, cls, behavior);
}
done:
log_and_fill_cache(cls, imp, sel, inst, curClass);
runtimeLock.unlock();
done_nolock:
if (slowpath((behavior & LOOKUP_NIL) && imp == forward_imp)) {
return nil;
}
return imp;
}
分析:
代码块 1:防止多线程干扰,再次从缓存中取一次,确保安全性
// Optimistic cache lookup
if (fastpath(behavior & LOOKUP_CACHE)) {
imp = cache_getImp(cls, sel);
if (imp) goto done_nolock;
}
代码块 2:确保类信息加载到内存中去了,并且创建类的双向链表,包括父类已经子类的各种信息
if (slowpath(!cls->isRealized())) {
cls = realizeClassMaybeSwiftAndLeaveLocked(cls, runtimeLock);
}
if (slowpath((behavior & LOOKUP_INITIALIZE) && !cls->isInitialized())) {
cls = initializeAndLeaveLocked(cls, inst, runtimeLock);
}
代码块 3:for无限循环,直到有 " break ",跳出循环
for (unsigned attempts = unreasonableClassCount();;) {
代码块 4:从curCkass类本身(有可能是父类,因为存在下面面的重新赋值情况)methodList中查找sel,找到的话,返回imp,跳出循环
Method meth = getMethodNoSuper_nolock(curClass, sel);
if (meth) {
imp = meth->imp;
goto done;
}
代码块 5:上面的代码块没有找到本类中的实例化方法,所以开始往父类开始找 curClass->superclass,并且判断此时的curClass如果为NSObject的时候,也就是NSObject->superclass == nil 的时候,将imp赋值为forward_imp ,然后跳出循环
if (slowpath((curClass = curClass->superclass) == nil)) {
// No implementation found, and method resolver didn't help.
// Use forwarding.
imp = forward_imp;
break;
}
代码块 6:从curClass类的缓存中取找imp
imp = cache_getImp(curClass, sel);
代码块 7:如何在循环中并没找到imp,也就是此时的imp== forward_imp的情况下,循环走完了,那么接下来开始找 *动态方法决议流程*
代码块 8:动态方法决议
if (slowpath(behavior & LOOKUP_RESOLVER)) {
behavior ^= LOOKUP_RESOLVER;
return resolveMethod_locked(inst, sel, cls, behavior);
}
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);
}
static void resolveInstanceMethod(id inst, SEL sel, Class cls)
{
runtimeLock.assertUnlocked();
ASSERT(cls->isRealized());
SEL resolve_sel = @selector(resolveInstanceMethod:);
if (!lookUpImpOrNil(cls, resolve_sel, cls->ISA())) {
// Resolver not implemented.
return;
}
BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
bool resolved = msg(cls, resolve_sel, sel);
// Cache the result (good or bad) so the resolver doesn't fire next time.
// +resolveInstanceMethod adds to self a.k.a. cls
IMP imp = lookUpImpOrNil(inst, sel, cls);
if (resolved && PrintResolving) {
if (imp) {
_objc_inform("RESOLVE: method %c[%s %s] "
"dynamically resolved to %p",
cls->isMetaClass() ? '+' : '-',
cls->nameForLogging(), sel_getName(sel), imp);
}
else {
// Method resolver didn't add anything?
_objc_inform("RESOLVE: +[%s resolveInstanceMethod:%s] returned YES"
", but no new implementation of %c[%s %s] was found",
cls->nameForLogging(), sel_getName(sel),
cls->isMetaClass() ? '+' : '-',
cls->nameForLogging(), sel_getName(sel));
}
}
}
核心代码:
BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
bool resolved = msg(cls, resolve_sel, sel);
重新发送消息,再次确认是否有该sel的imp
- 动态方法决议(补救措施)
+ (BOOL)resolveInstanceMethod:(SEL)sel ;
如果在该类中并未实现sel对象方法,可在此方法中动态绑定sel,添加进methods里面
+ (BOOL)resolveInstanceMethod:(SEL)sel{
if (sel == @selector(say666)) {
NSLog(@"%@ 来了",NSStringFromSelector(sel));
IMP imp = class_getMethodImplementation(self, @selector(sayMaster));
Method sayMMethod = class_getInstanceMethod(self, @selector(sayMaster));
const char *type = method_getTypeEncoding(sayMMethod);
return class_addMethod(self, sel, imp, type);
}
return [super resolveInstanceMethod:sel];
}
+ (BOOL)resolveClassMethod:(SEL)sel;
类方法找不到的补救措施
+ (BOOL)resolveClassMethod:(SEL)sel{
if (sel == @selector(sayTest)) {
NSLog(@"%@ 来了",NSStringFromSelector(sel));
IMP imp = class_getMethodImplementation(objc_getMetaClass("LGPerson"), @selector(tset));
Method sayMMethod =class_getClassMethod(objc_getMetaClass("LGPerson"), @selector(tset));
const char *type = method_getTypeEncoding(sayMMethod);
return class_addMethod(objc_getMetaClass("LGPerson") , sel, imp, type);
}
return [super resolveClassMethod:sel];
}
当在此两种方法中实现了并绑定了新的sel之后,会重新发起lookupIMP的查找,这时候就能查到,这时候就不会崩溃了!!!!
消息转发
当上述的动态方法决议并没与触发的时候,这时候imp还是_objc_msgForward的时候,这时候进入消息转发流程
-
快速转发过程
-
慢速转发过程
利用日志找到消息转发流程
bool logMessageSend(bool isClassMethod,
const char *objectsClass,
const char *implementingClass,
SEL selector)
{
char buf[ 1024 ];
// Create/open the log file
if (objcMsgLogFD == (-1))
{
snprintf (buf, sizeof(buf), "/tmp/msgSends-%d", (int) getpid ());
objcMsgLogFD = secure_open (buf, O_WRONLY | O_CREAT, geteuid());
if (objcMsgLogFD < 0) {
// no log file - disable logging
objcMsgLogEnabled = false;
objcMsgLogFD = -1;
return true;
}
}
// Make the log entry
snprintf(buf, sizeof(buf), "%c %s %s %s\n",
isClassMethod ? '+' : '-',
objectsClass,
implementingClass,
sel_getName(selector));
objcMsgLogLock.lock();
write (objcMsgLogFD, buf, strlen(buf));
objcMsgLogLock.unlock();
// Tell caller to not cache the method
return false;
}
#import <Foundation/Foundation.h>
#import "LGPerson.h"
// 慢速查找
extern void instrumentObjcMessageSends(BOOL flag);
int main(int argc, const char * argv[]) {
@autoreleasepool {
LGPerson *person = [LGPerson alloc];
//开启日志
instrumentObjcMessageSends(YES);
[person sayHello];
//关闭日志
instrumentObjcMessageSends(NO);
NSLog(@"Hello, World!");
}
return 0;
}
运行项目,之后跑道/tmp目录下查找


+ LGPerson NSObject resolveInstanceMethod:
+ LGPerson NSObject resolveInstanceMethod:
- LGPerson NSObject forwardingTargetForSelector:
- LGPerson NSObject forwardingTargetForSelector:
- LGPerson NSObject methodSignatureForSelector:
- LGPerson NSObject methodSignatureForSelector:
forwardingTargetForSelector(快速)
和 methodSignatureForSelector(慢速)
为消息转发过程

扩展
汇编层面,为什么收到_objc_msgForward的imp会报如下的错误,看下图理解
ENTRY __objc_msgForward
// Non-stret version
movq __objc_forward_handler(%rip), %r11
jmp *%r11
__attribute__((noreturn, cold)) void
objc_defaultForwardHandler(id self, SEL sel)
{
_objc_fatal("%c[%s %s]: unrecognized selector sent to instance %p "
"(no message forward handler is installed)",
class_isMetaClass(object_getClass(self)) ? '+' : '-',
object_getClassName(self), sel_getName(sel), self);
}
void *_objc_forward_handler = (void*)objc_defaultForwardHandler;
2020-11-24 16:24:29.132416+0800 KCObjc[15111:196925] +[LGPerson sss]: unrecognized selector sent to class 0x100002298
2020-11-24 16:24:29.134042+0800 KCObjc[15111:196925] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '+[LGPerson sss]: unrecognized selector sent to class 0x100002298'
*** First throw call stack:
(
0 CoreFoundation 0x00007fff34d7eb57 __exceptionPreprocess + 250
1 libobjc.A.dylib 0x000000010038db8a objc_exception_throw + 42
2 CoreFoundation 0x00007fff34dfdb37 __CFExceptionProem + 0
3 CoreFoundation 0x00007fff34ce33bb ___forwarding___ + 1427
4 CoreFoundation 0x00007fff34ce2d98 _CF_forwarding_prep_0 + 120
5 libobjc.A.dylib 0x00000001003da7b0 +[NSObject performSelector:] + 64
6 KCObjc 0x0000000100000b4a main + 74
7 libdyld.dylib 0x00007fff6ed72cc9 start + 1
8 ??? 0x0000000000000001 0x0 + 1
)
网友评论