我们都知道当我们用[]
调用方法时,编译器会修改为objc_msgSend
。
这个我们可以clang
看看就知道了。
clang person new
而根据具体情况,编译器会将消息发送修改为四种情况之一
objc_msgSend
objc_msgSend_stret
objc_msgSendSuper
objc_msgSendSuper_stret
比如我们调用[super xxx]
的时候,则会改为objc_msgSendSuper
的形式,而带有stret
,表明方法返回值是一个结构体类型。
objc_msgSend
通过源码来看,objc_msgSend
使用汇编写的,脑袋瓜子嗡嗡的。
结合注释,网上资料简单捋捋,看一下arm64里的汇编源码,基于objc4-779.1
源码
ENTRY _objc_msgSend
UNWIND _objc_msgSend, NoFrame
/// 检测receiver是否为nil。如果为nil,则进入LNilOrTagged
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
/// 如果不为nil,则现将receiver的isa存入p13;
ldr p13, [x0] // p13 = isa
/// 取出isa中的class,放到p16中
GetClassFromIsa_p16 p13 // p16 = class
LGetIsaDone:
// calls imp or objc_msgSend_uncached
/// 调用CacheLookup NORMAL 方法内部查找cache,如果未命中,则进入objc_msgSend_uncached
CacheLookup NORMAL, _objc_msgSend
#if SUPPORT_TAGGED_POINTERS
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 x16, [x10, x11, LSL #3]
adrp x10, _OBJC_CLASS_$___NSUnrecognizedTaggedPointer@PAGE
add x10, x10, _OBJC_CLASS_$___NSUnrecognizedTaggedPointer@PAGEOFF
cmp x10, x16
b.ne LGetIsaDone
// ext tagged
adrp x10, _objc_debug_taggedpointer_ext_classes@PAGE
add x10, x10, _objc_debug_taggedpointer_ext_classes@PAGEOFF
ubfx x11, x0, #52, #8
ldr x16, [x10, x11, LSL #3]
b LGetIsaDone
// SUPPORT_TAGGED_POINTERS
#endif
LReturnZero:
// x0 is already zero
mov x1, #0
movi d0, #0
movi d1, #0
movi d2, #0
movi d3, #0
ret
END_ENTRY _objc_msgSend
未命中缓存即进入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
objc_msgSend_uncached
内部调用MethodTableLookup
,而MethodTableLookup
里会调用lookUpImpOrForward
寻找class
的IMP
实现或进行消息转发。
lookUpImpOrForward 进入正题
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
// 先在cache中查找imp,找到就去 done_nolock,返回imp
if (fastpath(behavior & LOOKUP_CACHE)) {
imp = cache_getImp(cls, sel);
if (imp) goto done_nolock;
}
runtimeLock.lock();
if (slowpath(!cls->isRealized())) {
// 如果class没有被relize,先relize
cls = realizeClassMaybeSwiftAndLeaveLocked(cls, runtimeLock);
// runtimeLock may have been dropped but is now locked again
}
if (slowpath((behavior & LOOKUP_INITIALIZE) && !cls->isInitialized())) {
// 如果class没有init,则先init
cls = initializeAndLeaveLocked(cls, inst, runtimeLock);
}
runtimeLock.assertLocked();
curClass = cls;
/// 沿着继承链向上寻找
for (unsigned attempts = unreasonableClassCount();;) {
// curClass method list.
/// 在当前class的method list中查找有无imp // 在class的方法列表methods中,根据SEL查找对应的imp
Method meth = getMethodNoSuper_nolock(curClass, sel);
if (meth) {
imp = meth->imp;
/// 找到了 去done ,将imp存储到当前class 的cache中
goto done;
}
if (slowpath((curClass = curClass->superclass) == nil)) {/// 无父类
// No implementation found, and method resolver didn't help.
// Use forwarding.
///找不到实现,方法解析器也没有帮助。
/// 使用转发imp。
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.
// 找super class的cache
imp = cache_getImp(curClass, sel);
if (slowpath(imp == forward_imp)) {
/// imp 是消息转发imp。停止搜索,返回imp
break;
}
if (fastpath(imp)) {
///找到了 去done ,将imp存储到当前class 的cache中
goto done;
}
}
// No implementation found. Try method resolver once.
/// 在class和其所有的super class,均未找到imp ,进入动态方法解析流程resolveMethod
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;
}
总结一下大致流程
- 先在
cache
中查找imp
,找到了返回imp
- 在当前
class
的method list
中查找有无imp
, 在class
的方法列表methods
中,根据SEL
查找对应的imp
- 找到了 ,将
imp
存储到当前class
的cache
中 - 在
class
的所有super classes
中查找imp
(先看Super class
的cache
,再看super class
的方法列表) - 找到了,同3
- 均未找到imp,进入动态方法解析流程
resolveMethod
接下来也看看objc_msgSendSuper
先看看它的定义
OBJC_EXPORT id _Nullable
objc_msgSendSuper(struct objc_super * _Nonnull super, SEL _Nonnull op, ...)
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0);
可以看到传入的是一个objc_super
结构体,结构如下:
struct objc_super {
/// Specifies an instance of a class.
__unsafe_unretained _Nonnull id receiver;
__unsafe_unretained _Nonnull Class super_class;
/* super_class is the first class to search */
};
receiver
:消息接收者,是当前class
super_class
:父类,从父类开始寻找方法,而略过当前类
所以 调用[super xxxx]
并不是说父类来调用方法,只是从父类开始寻找方法,然后发送到当前class
,最终作用对象,还是当前class
。
比如当我调用父类的方法时,clang来看看
可以看到确实如上所述,objc_super
结构体的 receiver
就是传的self
,即当前class
, 而super_class
则是当前class
的父类。
这也就是下面的经典面试题,输出为什么一致的原因了。(都输出[self class]
)
@interface Father : NSObject
@end
@implementation Father
@end
@interface Son : Father
- (void)showClass;
@end
@implementation Son
- (void)showClass {
NSLog(@"self class = %@, super class = %@", [self class], [super class]);
}
...
Son *son = [Son new];
[son showClass]; // 这里输出什么?
...
看看arm64 的汇编源码
ENTRY _objc_msgSendSuper
UNWIND _objc_msgSendSuper, NoFrame
ldp p0, p16, [x0] // p0 = real receiver, p16 = class
// calls imp or objc_msgSend_uncached
CacheLookup NORMAL, _objc_msgSendSuper
END_ENTRY _objc_msgSendSuper
这里面的p0 = real receiver
即是当前class
, 而p16 = class
的class
即为父类,
后面的流程 与 objc_msgSend
是一致的,只不过lookUpImpOrForward
传入的cls
是父类了
网友评论