一、什么是Runtime?
OC是一门动态类型的语言,它允许很多操作都推迟到程序运行时再进行
OC的动态性就是由Runtime来支撑和实现的,而Runtime是一套C语言的API,它封装了很多动态性相关的函数
我们平时编写的OC代码,其实底层都是将代码转换成了Runtime API来进行调用
二、OC的消息机制
OC的方法调用其实都是转成了objc_msgSend函数的调用,给方法的调用者发送了一条消息。
objc_msgSend底层有3个阶段
- 消息发送(当前类、父类中查找)
- 动态方法解析
- 消息转发
三、流程解析
例如,我们有一个Person类,将这个Person实例化对象
先导入头文件
#import <objc/runtime.h>
#import <objc/message.h>
#import "Person.h"
//oc写法
Person *person1 = [[Person alloc] init];
//runtime写法
Person *person2 = objc_msgSend(objc_msgSend([Person class], @selector(alloc)), @selector(init));
新工程这里会报错
Too many arguments to function cal
这个问题只需要在FuDemo->Target中Build Setting的Enable Strict Checking of objc_msgSend Calls
的值设置为NO
即可。
运行程序,我们能看到,person1与person2都创建成功了,接下来就看看汇编代码片段是不是执行了objc_msgSend方法。首先将Person1的初始化代码注释掉,然后打开Always Show Disassembly
,让我们在调试时,断点能直接进入到汇编代码界面,如下图:
从汇编代码界面,我们能看到如下信息:
Person进行了alloc,然后该对象调用了
objc_msgSend
进行init
。将断点执行到调用objc_msgSend方法,‘按住Control + step into’查看
objc_msgSend
内部实现,再用同样方法查看objc_msgSend_uncached
,最终我们可以找到class_lookupMethodAndLoadCache3
这样的调用步骤。
四、objc_msgSend执行流程
工程搜索objc_msgSend
,找到objc-msg-x86_64.s
我们可以找到如下代码片段
/********************************************************************
*
* id objc_msgSend(id self, SEL _cmd,...);
* IMP objc_msgLookup(id self, SEL _cmd, ...);
*
* objc_msgLookup ABI:
* IMP returned in r11
* Forwarding returned in Z flag
* r10 reserved for our use but not used
*
********************************************************************/
这下面就是实现的主要步骤,我们抽取主要信息查看
/*进入到objc_msgSend*/
ENTRY _objc_msgSend
/*查找当前isa*/
GetIsaFast NORMAL // r10 = self->isa
/*从缓存中查找当前方法,如果查找到了IMP将结果返回给调用者*/
CacheLookup NORMAL, CALL // calls IMP on success
/*在缓存中没找到,搜索方法列表*/
// cache miss: go search the method lists
LCacheMiss:
/*\*/
// isa still in r10
/*跳转objc_msgSend_uncached*/
jmp __objc_msgSend_uncached
objc_msgSend_uncached
内基本都是在调用MethodTableLookup(从当前方法列表中查找)
。如果找到,则将IMP放到寄存器中。
MethodTableLookup
能找到class_lookupMethodAndLoadCache3
被调用,正好这也是之前我们所验证的最后一部。
在class_lookupMethodAndLoadCache3
中只做了lookUpImpOrForward(查找方法实现)
这一件事
因此runtime的执行顺序为:
1.objc_msgSend
2.CacheLookup(有缓存IMP,则返回给调用者。没有缓存则往下执行)
3.objc_msgSend_unCached
4.MethodTableLookup
5.class_lookupMethodAndLoadCache3
6.lookUpImpOrForward
五、lookUpImpOrForward代码片段注释
IMP lookUpImpOrForward(Class cls, SEL sel, id inst,
bool initialize, bool cache, bool resolver)
{
/*从缓存中查找*/
if (cache) {
imp = cache_getImp(cls, sel);
if (imp) return imp;
}
/*确保当前isa初始化*/
checkIsKnownClass(cls);
if (!cls->isRealized()) {
realizeClass(cls);
}
if (initialize && !cls->isInitialized()) {
runtimeLock.unlock();
_class_initialize (_class_getNonMetaClass(cls, inst));
runtimeLock.lock();
}
retry:
runtimeLock.assertLocked();
/*从当前类的缓存中查找*/
imp = cache_getImp(cls, sel);
if (imp) goto done;
/*从当前类的方法列表中查找*/
{
Method meth = getMethodNoSuper_nolock(cls, sel);
if (meth) {
/*将查找到的meth放入缓存中*/
log_and_fill_cache(cls, meth->imp, sel, inst, cls);
imp = meth->imp;
goto done;
}
}
/*沿着继承链向上查找*/
{
unsigned attempts = unreasonableClassCount();
for (Class curClass = cls->superclass;
curClass != nil;
curClass = curClass->superclass)
{
if (--attempts == 0) {
_objc_fatal("Memory corruption in class list.");
}
imp = cache_getImp(curClass, sel);
if (imp) {
if (imp != (IMP)_objc_msgForward_impcache) {
log_and_fill_cache(cls, imp, sel, inst, curClass);
goto done;
}
else {
break;
}
}
Method meth = getMethodNoSuper_nolock(curClass, sel);
if (meth) {
log_and_fill_cache(cls, meth->imp, sel, inst, curClass);
imp = meth->imp;
goto done;
}
}
}
/*没有找到IMP,尝试动态决议 resolveInstanceMethod*/
if (resolver && !triedResolver) {
runtimeLock.unlock();
_class_resolveMethod(cls, sel, inst);
runtimeLock.lock();
triedResolver = YES;
goto retry;
}
/*没有实现动态决议方法,触发消息转发流程 (forwardingTargetForSelector和forwardInvocation)*/
imp = (IMP)_objc_msgForward_impcache;
cache_fill(cls, sel, imp, inst);
done:
runtimeLock.unlock();
return imp;
}
网友评论