閱讀原文請到我的博客OC消息機(jī)制和super關(guān)鍵字
消息發(fā)送
在Objective-C里面調(diào)用一個方法[object method]
,運(yùn)行時會將它翻譯成objc_msgSend(id self, SEL op, ...)
的形式。
objc_msgSend
objc_msgSend
的實(shí)現(xiàn)在objc-msg-arm.s
昆码、objc-msg-arm64.s
等文件中,是通過匯編實(shí)現(xiàn)的延塑。這里主要看在arm64
即objc-msg-arm64.s
的實(shí)現(xiàn)蠢护。由于匯編不熟堤结,里面的實(shí)現(xiàn)只能連看帶猜必怜。
ENTRY _objc_msgSend
UNWIND _objc_msgSend, NoFrame
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 x16, x13, #ISA_MASK // x16 = class
LGetIsaDone:
CacheLookup NORMAL // calls imp or objc_msgSend_uncached
LNilOrTagged:
/* nil check洼畅,如果為空就是調(diào)用LReturnZero,LReturnZero里調(diào)用MESSENGER_END_NIL*/
b.eq LReturnZero // nil check
// tagged
mov x10, #0xf000000000000000
cmp x0, x10
b.hs LExtTag
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]
b LGetIsaDone
LExtTag:
// 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
LReturnZero:
// x0 is already zero
mov x1, #0
movi d0, #0
movi d1, #0
movi d2, #0
movi d3, #0
MESSENGER_END_NIL
ret
END_ENTRY _objc_msgSend
上面的流程可能是這樣的:
從CacheLookup
的注釋有兩處:
calls imp or objc_msgSend_uncached
Locate the implementation for a selector in a class method cache.
即使看不懂匯編代碼棚赔,但是從上面的注釋我們可以猜測,消息機(jī)制會先從緩存中去查找。
__objc_msgSend_uncached
通過方法名我們可以知道靠益,沒有緩存的時候應(yīng)該會執(zhí)行__objc_msgSend_uncached
丧肴。
STATIC_ENTRY __objc_msgSend_uncached
UNWIND __objc_msgSend_uncached, FrameWithNoSaves
// THIS IS NOT A CALLABLE C FUNCTION
// Out-of-band x16 is the class to search
MethodTableLookup
br x17
END_ENTRY __objc_msgSend_uncached
這里的MethodTableLookup
里涉及到objc-runtime-new.mm
文件中的_class_lookupMethodAndLoadCache3
。該函數(shù)會調(diào)用lookUpImpOrForward
函數(shù)胧后。
lookUpImpOrForward
lookUpImpOrForward
會返回一個imp
芋浮,它的函數(shù)實(shí)現(xiàn)比較長,但是注釋寫的非常清楚壳快。它的實(shí)現(xiàn)主要由以下幾步(這里直接從緩存獲取開始):
- 通過
cache_getImp
從緩存中獲取方法纸巷,有則返回,否則進(jìn)入第2步眶痰; - 通過
getMethodNoSuper_nolock
從類的方法列表中獲取瘤旨,有加入緩存中并返回,否則進(jìn)入第3步竖伯; - 通過父類的緩存和父類的方法列表中尋找是否有對應(yīng)的imp存哲,此時會進(jìn)入一個
for
循環(huán),沿著類的父類一直往上找七婴,直接找到NSObject為止祟偷。如果找到返回,否則進(jìn)入第4步打厘; - 進(jìn)入方法決議(method resolve)的過程即調(diào)用
_class_resolveMethod
修肠,如果失敗,進(jìn)入第5步户盯; - 在緩存嵌施、當(dāng)前類、父類以及方法決議都沒有找到的情況下先舷,Objective-C還為我們提供了最后一次翻身的機(jī)會艰管,調(diào)用
_objc_msgForward_impcache
進(jìn)行方法轉(zhuǎn)發(fā),如果找到便加入緩存蒋川;如果沒有就crash牲芋。
上述過程中有幾個比較重要的函數(shù):
_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);
}
}
}
上述函數(shù)會根據(jù)當(dāng)前傳入的類的是不是一個元類,在_class_resolveInstanceMethod
和_class_resolveClassMethod
中選擇一個進(jìn)行調(diào)用捺球。注釋也說明了這兩個方法的作用就是判斷當(dāng)前類是否實(shí)現(xiàn)了 resolveInstanceMethod:
或者resolveClassMethod:
方法缸浦,然后用objc_msgSend
執(zhí)行上述方法。
_class_resolveClassMethod
_class_resolveClassMethod
和_class_resolveInstanceMethod
實(shí)現(xiàn)類似氮兵,這里就只看_class_resolveClassMethod
的實(shí)現(xiàn)裂逐。
static void _class_resolveClassMethod(Class cls, SEL sel, id inst) {
assert(cls->isMetaClass());
if (! lookUpImpOrNil(cls, SEL_resolveClassMethod, inst,
NO/*initialize*/, YES/*cache*/, NO/*resolver*/)) {
//沒有找到resolveClassMethod方法,直接返回泣栈。
return;
}
BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
bool resolved = msg(_class_getNonMetaClass(cls, inst),
SEL_resolveClassMethod, sel);
// 緩存結(jié)果
IMP imp = lookUpImpOrNil(cls, sel, inst,
NO/*initialize*/, YES/*cache*/, NO/*resolver*/);
// 以下代碼省略不影響閱讀
}
_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
ENTRY __objc_msgForward
adrp x17, __objc_forward_handler@PAGE
ldr x17, [x17, __objc_forward_handler@PAGEOFF]
br x17
END_ENTRY __objc_msgForward
_objc_msgForward_impcache
用來進(jìn)行消息轉(zhuǎn)發(fā)卜高,但是其真正的核心是調(diào)用_objc_msgForward
弥姻。
消息轉(zhuǎn)發(fā)
關(guān)于_objc_msgForward
在objc
中并沒有其相關(guān)實(shí)現(xiàn),只能看到_objc_forward_handler
掺涛。其實(shí)_objc_msgForward
的實(shí)現(xiàn)是在CFRuntime.c
中的庭敦,但是開源出來的CFRuntime.c
并沒有相關(guān)實(shí)現(xiàn),但是也不影響我們對真理的追求薪缆。
我們做幾個實(shí)驗(yàn)來驗(yàn)證消息轉(zhuǎn)發(fā)秧廉。
消息重定向測試
// .h文件
@interface AObject : NSObject
- (void)sendMessage;
@end
// .m文件
@implementation AObject
/** 驗(yàn)證消息重定向 */
- (id)forwardingTargetForSelector:(SEL)aSelector {
if (aSelector == @selector(sendMessage)) {
return [BObject new];
}
return [super forwardingTargetForSelector:aSelector];
}
@end
// .h文件
@interface BObject : NSObject
- (void)sendMessage;
@end
// .m文件
@implementation BObject
- (void)sendMessage {
NSLog(@"%@ send message", self.class);
}
@end
// 調(diào)用
AObject *a = [AObject new];
[a sendMessage];
運(yùn)行結(jié)果:
2019-03-12 10:18:54.252949+0800 iOSCodeLearning[18165:5967575] BObject send message
在forwardingTargetForSelector:
處打個斷點(diǎn),查看一下調(diào)用棧:
_CF_forwarding_prep_0
和___forwarding___
這兩個方法會先被調(diào)用了拣帽,之后調(diào)用了forwardingTargetForSelector:
疼电。
方法簽名測試
// .h文件
@interface AObject : NSObject
- (void)sendMessage;
@end
// .m文件
@implementation AObject
/** 消息重定向 */
- (id)forwardingTargetForSelector:(SEL)aSelector {
return nil;
}
/** 方法簽名測試 */
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
if (aSelector == @selector(sendMessage)) {
return [BObject instanceMethodSignatureForSelector:@selector(sendMessage)];
}
return [super methodSignatureForSelector:aSelector];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation {
SEL selector = [anInvocation selector];
if (selector == @selector(sendMessage)) {
[anInvocation invokeWithTarget:[BObject new]];
} else {
[super forwardInvocation:anInvocation];
}
}
@end
// .h文件
@interface BObject : NSObject
- (void)sendMessage;
@end
// .m文件
@implementation BObject
- (void)sendMessage {
NSLog(@"%@ send message", self.class);
}
@end
// 調(diào)用
AObject *a = [AObject new];
[a sendMessage];
代碼執(zhí)行結(jié)果和消息重定向測試的運(yùn)行結(jié)果一致。_CF_forwarding_prep_0
和___forwarding___
這兩個方法又再次被調(diào)用了减拭,之后代碼會先執(zhí)行forwardingTargetForSelector:
(消息重定向)蔽豺,消息重定向如果失敗后調(diào)用methodSignatureForSelector:
和forwardInvocation:
方法簽名。所以說___forwarding___
方法才是消息轉(zhuǎn)發(fā)的真正實(shí)現(xiàn)峡谊。
crash測試
// .h文件
@interface AObject : NSObject
- (void)sendMessage;
@end
// .m文件
@implementation AObject
- (id)forwardingTargetForSelector:(SEL)aSelector {
return nil;
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
return [super methodSignatureForSelector:aSelector];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation {
}
/** 驗(yàn)證Crash */
- (void)doesNotRecognizeSelector:(SEL)aSelector {
if (aSelector == @selector(sendMessage)) {
NSLog(@"%@ doesNotRecognizeSelector", self.class);
}
}
@end
// .h文件
@interface BObject : NSObject
- (void)sendMessage;
@end
// .m文件
@implementation BObject
- (void)sendMessage {
NSLog(@"%@ send message", self.class);
}
@end
// 調(diào)用
AObject *a = [AObject new];
[a sendMessage];
代碼運(yùn)行結(jié)果肯定是crash茫虽,結(jié)合上面的代碼我們知道消息轉(zhuǎn)發(fā)會調(diào)用___forwarding___
這個內(nèi)部方法。___forwarding___
方法調(diào)用順序是forwardingTargetForSelector:
->methodSignatureForSelector:
->doesNotRecognizeSelector:
我們用一張圖表示整個消息發(fā)送的過程:
super關(guān)鍵字
我們先查看一下執(zhí)行[super init]
的時候既们,調(diào)用了那些方法
objc_msgSendSuper2
的聲明在objc-abi.h
中
// objc_msgSendSuper2() takes the current search class, not its superclass.
OBJC_EXPORT id _Nullable
objc_msgSendSuper2(struct objc_super * _Nonnull super, SEL _Nonnull op, ...)
OBJC_AVAILABLE(10.6, 2.0, 9.0, 1.0, 2.0);
objc_super
的定義如下:
struct objc_super {
/// Specifies an instance of a class.
__unsafe_unretained _Nonnull id receiver;
/// Specifies the particular superclass of the instance to message.
#if !defined(__cplusplus) && !__OBJC2__
/* For compatibility with old objc-runtime.h header */
__unsafe_unretained _Nonnull Class class;
#else
__unsafe_unretained _Nonnull Class super_class;
#endif
/* super_class is the first class to search */
};
從上面的定義我們可以知道receiver
即消息的實(shí)際接收者濒析,
super_class
為指向當(dāng)前類的父類。
所以該函數(shù)實(shí)際的操作是:從objc_super
結(jié)構(gòu)體指向的super_class
開始查找啥纸,直到會找到NSObject的方法為止号杏。找到后以receiver
去調(diào)用。當(dāng)然整個查找的過程還是和消息發(fā)送的流程一樣斯棒。
所以我們能理解為什么下面這段代碼執(zhí)行的結(jié)果都是AObject
了吧盾致。雖然使用[super class]
,但是真正執(zhí)行方法的對象還是AObject
荣暮。
// 代碼
@implementation AObject
- (instancetype)init {
if (self = [super init]) {
NSLog(@"%@", [super class]);
NSLog(@"%@", [self class]);
}
return self;
}
@end
// 執(zhí)行結(jié)果
2019-03-12 19:44:46.003313+0800 iOSCodeLearning[34431:7234182] AObject
2019-03-12 19:44:46.003442+0800 iOSCodeLearning[34431:7234182] AObject