这里介绍的Runtime是Modern版本
对应的编程接口:Objective-C 2.0
对应的平台:iPhone程序和Mac OS X V10.5及以后的系统中的64位程序
Runtime 基本是用 C、C++ 和汇编写的,消息发送过程由汇编和C两部分代码实现的,我们先来看汇编部分(以ARM64架构为例)。
打开项目搜索_objc_msgSend
关键字,打开objc-msg-arm64.s
源文件。
1.ENTRY _objc_msgSend
消息发送函数的入口
2.cmp x0,
:cmp
比较指令,x0
存储的是recevier 消息接收者,检查消息接收者是否为nil或是Tagged Pointer。
通过比较指令检查消息接收者是否为nil或是Tagged Pointer。
3.b.le LNilOrTagged
:b
跳转指令,通常和cmp
搭配使用,命中则跳转到LNilOrTagged
。
4.ldr x13, [x0]
:ldr
取值指令,从x0
指向的地址里面取出值存入 x13
,x13
中存的是isa指针地址,这里的isa
在ARM64架构是优化过的指针。
5.and x9, x13
:通过掩码进行与运算找到对应的类
6.CacheLookup NORMAL
:CacheLookup
汇编中定义的宏,NORMAL
参数,在类的缓存列表中查找方法名称,如果找到就调用方法实现(calls imp),否则到类的方法列表中查找方法名称(__objc_msgSend_uncached_impcache
)
ENTRY _objc_msgSend
MESSENGER_START
cmp x0, #0 // nil check and tagged pointer check
b.le LNilOrTagged // (MSB tagged pointer looks negative)
ldr x13, [x0] // x13 = isa
and x9, x13, #ISA_MASK // x9 = class
LGetIsaDone:
CacheLookup NORMAL // calls imp or objc_msgSend_uncached
检查消息接收者是否为nil或是Tagged Pointer
1.b.eq LReturnZero
:如果消息接收者为nil,跳转到LReturnZero
直接return
结束当前的消息发送。
这里就解释了为什么给空对象发送消息不会Crash
2.b LGetIsaDone
:如果是Tagged Pointer完成获取isa所指的对象后跳回_objc_msgSend
继续执行
LNilOrTagged:
b.eq LReturnZero // nil check
// tagged
adrp x10, _objc_debug_taggedpointer_classes@PAGE
add x10, x10, _objc_debug_taggedpointer_classes@PAGEOFF
ubfx x11, x0, #60, #4
ldr x9, [x10, x11, LSL #3]
b LGetIsaDone
LReturnZero:
// x0 is already zero
mov x1, #0
movi d0, #0
movi d1, #0
movi d2, #0
movi d3, #0
MESSENGER_END_NIL
ret // return
END_ENTRY _objc_msgSend // 消息发送结束
在缓存中查找
CacheLookup宏定义
1.CacheHit
命中调用方法实现或返回imp
2.CheckMiss
未命中到类的方法列表中查找方法名称(__objc_msgSend_uncached_impcache)
.macro CacheLookup
// x1 = SEL, x9 = isa
ldp x10, x11, [x9, #CACHE] // x10 = buckets, x11 = occupied|mask
and w12, w1, w11 // x12 = _cmd & mask
add x12, x10, x12, LSL #4 // x12 = buckets + ((_cmd & mask)<<4)
ldp x16, x17, [x12] // {x16, x17} = *bucket
1: cmp x16, x1 // if (bucket->sel != _cmd)
b.ne 2f // scan more
CacheHit $0 // call or return imp
2: // not hit: x12 = not-hit bucket
CheckMiss $0 // miss if bucket->cls == 0
cmp x12, x10 // wrap if bucket == buckets
b.eq 3f
ldp x16, x17, [x12, #-16]! // {x16, x17} = *--bucket
b 1b // loop
……
.endmacro
CacheHit宏定义
br x17
:调用方法实现
.macro CacheHit
MESSENGER_END_FAST
.if $0 == NORMAL
br x17 // call imp
.else
b LGetImpHit
.endif
.endmacro
CheckMiss宏定义
cbz x16, __objc_msgSend_uncached_impcache
:到类的方法列表中查找方法名称
.macro CheckMiss
.if $0 == NORMAL // miss if bucket->cls == 0
cbz x16, __objc_msgSend_uncached_impcache
.else
cbz x16, LGetImpMiss
.endif
.endmacro
在类的方法列表中查找方法名称
bl __class_lookupMethodAndLoadCache3
:__class_lookupMethodAndLoadCache3
是C中实现的函数,会跳转到函数中在类的方法列表中查找方法名称和方法实现,找到后会返回汇编部分继续执行。
STATIC_ENTRY __objc_msgSend_uncached_impcache
……
// receiver and selector already in x0 and x1
mov x2, x9
bl __class_lookupMethodAndLoadCache3
// imp in x0
mov x17, x0
……
END_ENTRY __objc_msgSend_uncached_impcache
汇编部分这此就结束了。
在方法列表中查找
注意搜索关键字_class_lookupMethodAndLoadCache3
前面少了一个下划线_
,这是汇编和C的一个命名约定,打开新版的文件objc-runtime-new.mm
。
// id 对象
// sel 方法名称
// cls 实例对象类
IMP _class_lookupMethodAndLoadCache3(id obj, SEL sel, Class cls)
{
return lookUpImpOrForward(cls, sel, obj,
YES/*initialize*/, NO/*cache*/, YES/*resolver*/);
}
因为_class_lookupMethodAndLoadCache3传入的cache=NO,所以这里会直接跳过if中代码的执行,在objc_msgSend中已经使用汇编代码查找过了。
runtimeLock.assertUnlocked();
// Optimistic cache lookup
if (cache) {
imp = cache_getImp(cls, sel);
if (imp) return imp;
}
初始化的过程中会对其中的类进行第一次初始化也就是执行realizeClass
方法,为类分配可读写结构体class_rw_t
的空间,并返回正确的类结构体。
if (!cls->isRealized()) {
runtimeLock.unlockRead();
runtimeLock.write();
realizeClass(cls);
runtimeLock.unlockWrite();
runtimeLock.read();
}
_class_initialize
方法会调用类的initialize方法进行初始化
if (initialize && !cls->isInitialized()) {
runtimeLock.unlockRead();
_class_initialize (_class_getNonMetaClass(cls, inst));
runtimeLock.read();
}
再次从cache中查找方法的实现,如果找到就会跳转到done标签,返回imp
方法实现。cache_getImp
又回到汇编部分,它会进入一个CacheLookup
的标签。
imp = cache_getImp(cls, sel);
if (imp) goto done;
从类的方法列表中寻找方法的实现
Method meth = getMethodNoSuper_nolock(cls, sel);
if (meth) {
log_and_fill_cache(cls, meth->imp, sel, inst, cls);
imp = meth->imp;
goto done;
}
调用getMethodNoSuper_nolock
方法查找对应的方法的结构体指针method_t
static method_t * getMethodNoSuper_nolock(Class cls, SEL sel)
{
runtimeLock.assertLocked();
assert(cls->isRealized());
for (auto mlists = cls->data()->methods.beginLists(),
end = cls->data()->methods.endLists();
mlists != end;
++mlists)
{
method_t *m = search_method_list(*mlists, sel);
if (m) return m;
}
return nil;
}
类中数据的方法列表methods是一个二维数组method_array_t
,写一个for循环遍历整个方法列表
static method_t *search_method_list(const method_list_t *mlist, SEL sel)
{
int methodListIsFixedUp = mlist->isFixedUp();
int methodListHasExpectedSize = mlist->entsize() == sizeof(method_t);
if (__builtin_expect(methodListIsFixedUp && methodListHasExpectedSize, 1)) {
return findMethodInSortedMethodList(sel, mlist);
} else {
// Linear search of unsorted method list
for (auto& meth : *mlist) {
if (meth.name == sel) return &meth;
}
}
return nil;
}
在已排序的方法列表中查找method
,返回方法结构体method_t。
static method_t *findMethodInSortedMethodList(SEL key, const method_list_t *list){
const method_t * const first = &list->first;
const method_t *base = first;
const method_t *probe;
uintptr_t keyValue = (uintptr_t)key;
uint32_t count;
for (count = list->count; count != 0; count >>= 1) {
probe = base + (count >> 1);
uintptr_t probeValue = (uintptr_t)probe->name;
if (keyValue == probeValue) {
while (probe > first && keyValue == (uintptr_t)probe[-1].name) {
probe--;
}
return (method_t *)probe;
}
if (keyValue > probeValue) {
base = probe + 1;
count--;
}
}
return nil;
}
如果在这里找到了方法的实现,将它加入类的缓存中,这个操作最后是由log_and_fill_cache
方法来完成的,并跳转到done
标签返回方法实现。
static void log_and_fill_cache(Class cls, IMP imp, SEL sel, id receiver, Class implementer){
#if SUPPORT_MESSAGE_LOGGING
if (objcMsgLogEnabled) {
bool cacheIt = logMessageSend(implementer->isMetaClass(),
cls->nameForLogging(),
implementer->nameForLogging(),
sel);
if (!cacheIt) return;
}
#endif
cache_fill (cls, sel, imp, receiver);
}
在父类中寻找实现
先查找父类缓存,未命中再搜索方法列表。
unsigned attempts = unreasonableClassCount();
for (Class curClass = cls->superclass;
curClass != nil;
curClass = curClass->superclass){
// Halt if there is a cycle in the superclass chain.
if (--attempts == 0) {
_objc_fatal("Memory corruption in class list.");
}
// Superclass cache.
imp = cache_getImp(curClass, sel);
if (imp) {
if (imp != (IMP)_objc_msgForward_impcache) {
// Found the method in a superclass. Cache it in this class.
log_and_fill_cache(cls, imp, sel, inst, curClass);
goto done;
}
else {
// Found a forward:: entry in a superclass.
// Stop searching, but don't cache yet; call method
// resolver for this class first.
break;
}
}
// Superclass method list.
Method meth = getMethodNoSuper_nolock(curClass, sel);
if (meth) {
log_and_fill_cache(cls, meth->imp, sel, inst, curClass);
imp = meth->imp;
goto done;
}
}
方法解析
消息发送方法实现如果没有找到会尝试一次方法解析。
// No implementation found. Try method resolver once.
if (resolver && !triedResolver) {
runtimeLock.unlockRead();
_class_resolveMethod(cls, sel, inst);
runtimeLock.read();
// 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;
}
解析类方法或解析实例方法。
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);
}
}
}
在执行了方法解析之后,会跳转到retry标签,重新执行查找方法实现的流程,只不过不会再调用resolveInstanceMethod:方法了(将triedResolver标记为YES)。
消息转发
没有找到方法实现,动态方法解析也没有帮助,进入消息转发机制。
// No implementation found, and method resolver didn't help.
// Use forwarding.
imp = (IMP)_objc_msgForward_impcache;
cache_fill(cls, sel, imp, inst);
消息转发又进入了汇编部分,搜索__objc_msgForward_impcache
。
STATIC_ENTRY __objc_msgForward_impcache
MESSENGER_START
nop
MESSENGER_END_SLOW
// No stret specialization.
b __objc_msgForward
END_ENTRY __objc_msgForward_impcache
b __objc_msgForward
通过跳转指令跳到__objc_msgForward
方法
ENTRY __objc_msgForward
adrp x17, __objc_forward_handler@PAGE
ldr x17, [x17, __objc_forward_handler@PAGEOFF]
br x17
END_ENTRY __objc_msgForward
br x17
通过跳转指令又回到了C代码中 ,搜索_objc_forward_handler
。
// Default forward handler halts the process.
__attribute__((noreturn)) 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;
_objc_forward_handler
是个函数指针,并给了一个默认的函数实现。也就是我的方法没有实现会抛出错误信息。
打印runtime的消息
整个消息发送部分的源码就已经结束了,再往下的部分没有开源我们看不到。
我们可以通过消息打印的API,看到消息转发的内部执行过程。
// 声明外部打印函数
extern void instrumentObjcMessageSends(BOOL);
int main(int argc, const char * argv[]) {
@autoreleasepool {
// 打开打印消息
instrumentObjcMessageSends(YES);
[Person run];
// 关闭打印消息
instrumentObjcMessageSends(NO);
}
return 0;
}
打印结果输出到 /private/tmp/msgSends-xxxx文件
终端执行 open msgSends-xxxx,就可看到函数的调用过程
+ Person NSObject initialize
+ Person NSObject resolveClassMethod:
+ Person NSObject resolveClassMethod:
+ NSObject NSObject resolveInstanceMethod:
+ NSObject NSObject resolveInstanceMethod:
+ Person Person forwardingTargetForSelector:
+ Person Person forwardingTargetForSelector:
+ NSObject NSObject forwardingTargetForSelector:
+ Person Person methodSignatureForSelector:
+ Person Person methodSignatureForSelector:
+ NSMethodSignature NSObject initialize
+ NSMethodSignature NSMethodSignature signatureWithObjCTypes:
+ NSMethodSignature NSObject alloc
- NSMethodSignature NSMethodSignature _typeString
- NSMethodSignature NSMethodSignature _frameDescriptor
- NSTaggedPointerString NSTaggedPointerString retain
- NSMethodSignature NSObject release
- NSMethodSignature NSMethodSignature _frameDescriptor
+ Person NSObject resolveClassMethod:
+ Person NSObject resolveClassMethod:
+ NSObject NSObject resolveInstanceMethod:
+ NSObject NSObject resolveInstanceMethod:
+ Person Person forwardInvocation:
+ NSInvocation NSObject initialize
+ NSInvocation NSInvocation _invocationWithMethodSignature:frame:
+ NSInvocation NSObject alloc
- NSMethodSignature NSObject retain
- NSMethodSignature NSMethodSignature frameLength
- NSMethodSignature NSMethodSignature _frameDescriptor
- NSMethodSignature NSMethodSignature frameLength
- NSMethodSignature NSMethodSignature _frameDescriptor
- NSInvocation NSObject autorelease
+ Person Person forwardInvocation:
- NSInvocation NSInvocation setSelector:
- NSInvocation NSInvocation setArgument:atIndex:
- NSMethodSignature NSMethodSignature numberOfArguments
- NSMethodSignature NSMethodSignature _frameDescriptor
- NSMethodSignature NSMethodSignature _argInfo:
- NSMethodSignature NSMethodSignature _frameDescriptor
- NSInvocation NSInvocation setTarget:
- NSInvocation NSInvocation setArgument:atIndex:
- NSMethodSignature NSMethodSignature numberOfArguments
- NSMethodSignature NSMethodSignature _frameDescriptor
- NSMethodSignature NSMethodSignature _argInfo:
- NSMethodSignature NSMethodSignature _frameDescriptor
- NSInvocation NSInvocation invoke
- NSInvocation NSInvocation getArgument:atIndex:
- NSMethodSignature NSMethodSignature numberOfArguments
- NSMethodSignature NSMethodSignature _frameDescriptor
- NSMethodSignature NSMethodSignature _argInfo:
- NSMethodSignature NSMethodSignature _frameDescriptor
- NSMethodSignature NSMethodSignature _frameDescriptor
- NSMethodSignature NSMethodSignature frameLength
- NSMethodSignature NSMethodSignature _frameDescriptor
+ Person Person run
- __NSCFConstantString __NSCFString _fastCStringContents:
- NSMethodSignature NSMethodSignature methodReturnType
- NSMethodSignature NSMethodSignature _argInfo:
- NSMethodSignature NSMethodSignature _frameDescriptor
网友评论