一套以c、c++以及汇编写成的,能够为Object-C提供运行时功能的api。源码参考c++
Runtime其实有两个版本: “modern” 和 “legacy”。咱们如今用的 Objective-C 2.0 采用的是现行 (Modern) 版的 Runtime 系统,只能运行在 iOS 和 macOS 10.5 以后的 64 位程序中。而 maxOS 较老的32位程序仍采用 Objective-C 1 中的(早期)Legacy 版本的 Runtime 系统。api
这两个版本最大的区别在于当你更改一个类的实例变量的布局时,在早期版本中你须要从新编译它的子类,而现行版就不须要。缓存
熟悉Object-C的同窗都知道,对象的方法调用在底层实际上是一个消息发送的过程,接下来咱们验证一下。多线程
首先定义一个对象Son,它包含一个实例方法。而后咱们在main.m中调用,再经过clang -rewrite-objc main.m,生成一个.cpp文件,对好比下:并发
--------------main.m---------------
Son *son = [Son new];
[son son_instanceSelector];
--------------main.cpp---------------
Son *son = ((Son *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("Son"), sel_registerName("new"));
((void (*)(id, SEL))(void *)objc_msgSend)((id)son, sel_registerName("son_instanceSelector"));
复制代码
能够分析得出,对象的方法调用在底层都被编译成了objc_msgSend(id _Nullable self, SEL _Nonnull op, …)app
再来看他的父类方法调用,声明两个类,Father、Son(继承Father),而后在Son的实例方法与类方法中分别实现父类对应的实例方法与类方法,而后在经过clang编译,比较:函数
-----------------------------Son.m-----------------------------
-(void)son_instanceSelector{
[super father_instanceSelector];
}
+(void)son_classSelector{
[super father_classSelector];
}
-----------------------------Son.cpp---------------------------
static void _I_Son_son_instanceSelector(Son * self, SEL _cmd) {
((void (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("Son"))}, sel_registerName("father_instanceSelector"));
}
static void _C_Son_son_classSelector(Class self, SEL _cmd) {
((void (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)(&(__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getMetaClass("Son"))}, sel_registerName("father_classSelector"));
}
复制代码
能够得出,super的方法调用在底层会转化为objc_msgSendSuper(struct objc_super * _Nonnull super, SEL _Nonnull op, ...),就是去父类的方法列表中查找方法,而后调用。oop
注意:方法调用的主体仍是子类对象。布局
因此,咱们平时的方法调用使用Runtime底层函数来实现是什么样子呢?以下:post
// 类对象实例方法调用
objc_msgSend(son, sel_registerName("son_instanceSelector"));
// 类方法调用
objc_msgSend(objc_getClass("Son"), sel_registerName("son_classSelector"));
// 向父类发消息(实例方法)
struct objc_super kmSuper;
kmSuper.receiver = son;
kmSuper.super_class = [Father class];
objc_msgSendSuper(&kmSuper, @selector(father_instanceSelector));
//向父类发消息(类方法)
struct objc_super myClassSuper;
myClassSuper.receiver = [son class];
myClassSuper.super_class = class_getSuperclass(objc_getMetaClass("Son"));
objc_msgSendSuper(&myClassSuper, NSSelectorFromString(@"father_classSelector"));
复制代码
objc_msgSend的快速查找流程是用汇编实现的,主要缘由有
objc_msgSend的汇编源码:
ENTRY _objc_msgSend
UNWIND _objc_msgSend, NoFrame
cmp p0, #0 // nil check and tagged pointer check
#if SUPPORT_TAGGED_POINTERS
b.le LNilOrTagged // (MSB tagged pointer looks negative)
#else
b.eq LReturnZero
#endif
// person - isa - 类
ldr p13, [x0] // p13 = isa
GetClassFromIsa_p16 p13 // p16 = class
LGetIsaDone:
CacheLookup NORMAL // calls imp or objc_msgSend_uncached
复制代码
接着查看CacheLookup函数源码:
.macro CacheLookup
// p1 = SEL, p16 = isa
ldp p10, p11, [x16, #CACHE] // p10 = buckets, p11 = occupied|mask
#if !__LP64__
and w11, w11, 0xffff // p11 = mask
#endif
and w12, w1, w11 // x12 = _cmd & mask
add p12, p10, p12, LSL #(1+PTRSHIFT)
// p12 = buckets + ((_cmd & mask) << (1+PTRSHIFT))
ldp p17, p9, [x12] // {imp, sel} = *bucket
1: cmp p9, p1 // if (bucket->sel != _cmd)
b.ne 2f // scan more
CacheHit $0 // call or return imp
2: // not hit: p12 = not-hit bucket
CheckMiss $0 // miss if bucket->sel == 0
cmp p12, p10 // wrap if bucket == buckets
b.eq 3f
ldp p17, p9, [x12, #-BUCKET_SIZE]! // {imp, sel} = *--bucket
b 1b // loop
3: // wrap: p12 = first bucket, w11 = mask
add p12, p12, w11, UXTW #(1+PTRSHIFT) // p12 = buckets + (mask << 1+PTRSHIFT)
// Clone scanning loop to miss instead of hang when cache is corrupt.
// The slow path may detect any corruption and halt later.
ldp p17, p9, [x12] // {imp, sel} = *bucket
1: cmp p9, p1 // if (bucket->sel != _cmd)
b.ne 2f // scan more
CacheHit $0 // call or return imp
2: // not hit: p12 = not-hit bucket
CheckMiss $0 // miss if bucket->sel == 0
cmp p12, p10 // wrap if bucket == buckets
b.eq 3f
ldp p17, p9, [x12, #-BUCKET_SIZE]! // {imp, sel} = *--bucket
b 1b // loop
3: // double wrap
JumpMiss $0
.endmacro
复制代码
一、经过isa偏移16位,拿到类的方法缓存中的buckets、以及occupied和mask(类的方法缓存)
二、查看是否缓存命中,有则retun imp
经过汇编查找方法缓存,缓存命中,就是方法查找的快速流程,未命中则开始走方法查找的慢速流程。
三、缓存未命中,调用CheckMiss函数
CheckMiss源码:
.macro CheckMiss
// miss if bucket->sel == 0
.if $0 == GETIMP
cbz p9, LGetImpMiss
.elseif $0 == NORMAL
cbz p9, __objc_msgSend_uncached
.elseif $0 == LOOKUP
cbz p9, __objc_msgLookup_uncached
.else
.abort oops
.endif
.endmacro
复制代码
由于咱们以前调用的CacheLookup NORMAL,因此会走到objc_msgSend_uncached:
STATIC_ENTRY __objc_msgSend_uncached
UNWIND __objc_msgSend_uncached, FrameWithNoSaves
// THIS IS NOT A CALLABLE C FUNCTION
// Out-of-band p16 is the class to search
MethodTableLookup
TailCallFunctionPointer x17
END_ENTRY __objc_msgSend_uncached
复制代码
里面仅有一段函数调用-MethodTableLookup:
.macro MethodTableLookup
// push frame
SignLR
stp fp, lr, [sp, #-16]!
mov fp, sp
// save parameter registers: x0..x8, q0..q7
sub sp, sp, #(10*8 + 8*16)
stp q0, q1, [sp, #(0*16)]
stp q2, q3, [sp, #(2*16)]
stp q4, q5, [sp, #(4*16)]
stp q6, q7, [sp, #(6*16)]
stp x0, x1, [sp, #(8*16+0*8)]
stp x2, x3, [sp, #(8*16+2*8)]
stp x4, x5, [sp, #(8*16+4*8)]
stp x6, x7, [sp, #(8*16+6*8)]
str x8, [sp, #(8*16+8*8)]
// receiver and selector already in x0 and x1
mov x2, x16
bl __class_lookupMethodAndLoadCache3
// IMP in x0
mov x17, x0
// restore registers and return
ldp q0, q1, [sp, #(0*16)]
ldp q2, q3, [sp, #(2*16)]
ldp q4, q5, [sp, #(4*16)]
ldp q6, q7, [sp, #(6*16)]
ldp x0, x1, [sp, #(8*16+0*8)]
ldp x2, x3, [sp, #(8*16+2*8)]
ldp x4, x5, [sp, #(8*16+4*8)]
ldp x6, x7, [sp, #(8*16+6*8)]
ldr x8, [sp, #(8*16+8*8)]
mov sp, fp
ldp fp, lr, [sp], #16
AuthenticateLR
.endmacro
复制代码
作了一些内存上的准备工做,而后调用函数**_class_lookupMethodAndLoadCache3:**
IMP _class_lookupMethodAndLoadCache3(id obj, SEL sel, Class cls)
{
return lookUpImpOrForward(cls, sel, obj,
YES/*initialize*/, NO/*cache*/, YES/*resolver*/);
}
复制代码
从这里开始,便从汇编进入到了C/C++。也就是真正的方法慢速查找流程。
在快速查找流程中,方法缓存未命中。也就是说,快速查找行不通的时候,底层就会走到慢速查找流程,并一路从汇编走到lookUpImpOrForward函数。
lookUpImpOrForward源码:
IMP lookUpImpOrForward(Class cls, SEL sel, id inst, bool initialize, bool cache, bool resolver) {
IMP imp = nil;
bool triedResolver = NO;
runtimeLock.assertUnlocked();
// 判断是否须要从缓存查找,是则先去方法缓存查找
if (cache) {
imp = cache_getImp(cls, sel);
if (imp) return imp;
}
// 加锁,防止多线程并发引发的返回错误
runtimeLock.lock();
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);
// 若是找到方法,先缓存,而后goto done(return imp)
if (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;
}
}
}
// 没有找到方法实现,调用一次方法动态解析
if (resolver && !triedResolver) {
runtimeLock.unlock();
_class_resolveMethod(cls, sel, inst);
runtimeLock.lock();
// Don't cache the result; we don't hold the lock so it may have
// changed already. Re-do the search from scratch instead.
triedResolver = YES;
goto retry;
}
// 未能找到方法实现,且方法的动态解析也没有用的时候
// 就会走到消息转发流程(下篇文章会讲)
imp = (IMP)_objc_msgForward_impcache;
cache_fill(cls, sel, imp, inst);
done:
runtimeLock.unlock();
return imp;
}
复制代码
方法的慢速查找遵循着一个规律,即先找类自己的方法,找不到则找父类方法,一直找到NSObject。
_class_resolveMethod:
void _class_resolveMethod(Class cls, SEL sel, id inst)
{
if (! cls->isMetaClass()) {
// try [cls resolveInstanceMethod:sel]
_class_resolveInstanceMethod(cls, sel, inst);
}
else {
// try [nonMetaClass resolveClassMethod:sel]
// and [cls resolveInstanceMethod:sel]
_class_resolveClassMethod(cls, sel, inst);
if (!lookUpImpOrNil(cls, sel, inst,
NO/*initialize*/, YES/*cache*/, NO/*resolver*/))
{
_class_resolveInstanceMethod(cls, sel, inst);
}
}
}
复制代码
若是咱们在类中实现了resolveInstanceMethod或者resolveClassMethod方法而且正确处理了sel,能够避免程序报错。他会返回一个方法实现imp,并让程序去再次查找。 例:
咱们在主线程中调用person对象一个并未实现的实例方法
Person *per = [Person alloc];
[per performSelector:@selector(run)];
复制代码
而后,在Person.m中加入动态解析函数:
----------------------Person.m----------------------
#import "Person.h"
#import <objc/runtime.h>
@implementation Person
+ (BOOL)resolveInstanceMethod:(SEL)sel{
if (sel == @selector(run)) {
class_addMethod(self, sel, (IMP)methodImp, "v@:");
return YES;
}
return NO;
}
void methodImp(id self,SEL _cmd){
NSLog(@"来了老弟...");
}
@end
复制代码
打印结果:
来了老弟...
resolveClassMethod方法同理。
方法查找的流程图以下: