在OC中,全部的方法調用底層都會轉換成objc_msgSend方法進行調用,那麼objc_msgSend底層是如何實現的呢?如今咱們就經過objc源碼來了解objc_msgSend的調用流程。c++
在objc源碼中查找objc_msgSend方法。發現方法的實如今objc-msg-arm64.s、objc-msg-i386.s等objc-msg開頭的文件中都存在,所以咱們主要查看objc-msg-arm64.s的實現。數組
在objc-msg-arm64.s中objc_msgSend是使用匯編來實現的,部分彙編代碼以下緩存
//進入 _objc_msgSend 函數
ENTRY _objc_msgSend
UNWIND _objc_msgSend, NoFrame
//檢查objc_msgSend第一個參數是否爲nil
cmp p0, #0 // nil check and tagged pointer check
#if SUPPORT_TAGGED_POINTERS
//若是支持tagged pointer,若是p0<=0,執行LNilOrTagged函數
b.le LNilOrTagged // (MSB tagged pointer looks negative)
#else
b.eq LReturnZero
#endif
ldr p13, [x0] // p13 = isa
GetClassFromIsa_p16 p13 // p16 = class
LGetIsaDone:
CacheLookup NORMAL // calls imp or objc_msgSend_uncached
#if SUPPORT_TAGGED_POINTERS
LNilOrTagged:
//判斷objc_msgSend第一個參數是否爲nil
b.eq LReturnZero // nil check
// tagged
......
cmp x10, x16
b.ne LGetIsaDone
// ext tagged
......
b LGetIsaDone
#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的第一個參數,也就是當前接收消息的對象進行nil判斷,而後根據比較結果,跳轉到LNilOrTagged,LNilOrTagged中會調用LGetIsaDone,在LGetIsaDone中則經過CacheLookup來調用方法的實現。bash
.macro CacheLookup
// p1 = SEL, p16 = isa(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
複製代碼
CacheLookup其實至關於OC中的宏,CacheLookup的功能其實就是去當前類的方法緩存中去查找方法,若是在緩存中找到方法,會調用CacheHit,若是沒有找到方法,則會調用CheckMiss,繼續查看CheckMiss的定義app
.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
複製代碼
發如今CheckMiss調用了__objc_msgSend_uncached函數
.endmacro
STATIC_ENTRY __objc_msgSend_uncached
UNWIND __objc_msgSend_uncached, FrameWithNoSaves
MethodTableLookup
TailCallFunctionPointer x17
END_ENTRY __objc_msgSend_uncached
複製代碼
在__objc_msgSend_uncached中,調用了MethodTableLookupoop
.macro MethodTableLookup
......
// receiver and selector already in x0 and x1
mov x2, x16
bl __class_lookupMethodAndLoadCache3
......
複製代碼
在MethodTableLookup中調用的__class_lookupMethodAndLoadCache3在彙編代碼中查找不到對應的實現,所以咱們在整個objc源碼中查詢class_lookupMethodAndLoadCache3的實現,最終找到C語言實現以下學習
IMP _class_lookupMethodAndLoadCache3(id obj, SEL sel, Class cls)
{
return lookUpImpOrForward(cls, sel, obj,
YES/*initialize*/, NO/*cache*/, YES/*resolver*/);
}
複製代碼
objc-runtime-new.mm中的lookUpImpOrForward函數就是咱們最終要找到的核心方法。ui
lookUpImpOrForward函數源碼以下:spa
IMP lookUpImpOrForward(Class cls, SEL sel, id inst,
bool initialize, bool cache, bool resolver)
{
IMP imp = nil;
//是否進行了方法解析操做
bool triedResolver = NO;
runtimeLock.assertUnlocked();
// 若是cache爲true,則會再次到當前類的緩存中查找方法
if (cache) {
imp = cache_getImp(cls, sel);
if (imp) return imp;
}
runtimeLock.lock();
checkIsKnownClass(cls);
//判斷當前類有沒有被實現(也就是類中的data()返回的是不是class_rw_t類型)
if (!cls->isRealized()) {
cls = realizeClassMaybeSwiftAndLeaveLocked(cls, runtimeLock);
}
//判斷當前類是否初始化,若是沒有初始化,會向類發送+initialize消息
if (initialize && !cls->isInitialized()) {
cls = initializeAndLeaveLocked(cls, inst, runtimeLock);
}
retry:
runtimeLock.assertLocked();
//再次到當前類的緩存列表中查找方法,若是找到,直接跳轉到done執行,由於可能會在運行時動態給類增長方法
imp = cache_getImp(cls, sel);
if (imp) goto done;
{
//到當前類的方法列表中查詢
Method meth = getMethodNoSuper_nolock(cls, sel);
if (meth) {
//若是找到方法,則將方法保存到當前類的緩存中去,而後跳轉到done執行
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)
{
// Halt if there is a cycle in the superclass chain.
if (--attempts == 0) {
_objc_fatal("Memory corruption in class list.");
}
// 先到父類中的緩存中查找
imp = cache_getImp(curClass, sel);
if (imp) {
//判斷imp是否就是函數指針_objc_msgForward_impcache
if (imp != (IMP)_objc_msgForward_impcache) {
//imp不是_objc_msgForward_impcache,將此方法保存到當前類的緩存中去,跳轉到done繼續執行
log_and_fill_cache(cls, imp, sel, inst, curClass);
goto done;
}
else {
//若是查找到的imp就是_objc_msgForward_impcache函數,那麼直接跳出循環
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();
resolveMethod(cls, sel, inst);
runtimeLock.lock();
//triedResolver代表當前動態方法解析只會執行一次
triedResolver = YES;
goto retry;
}
//若是當前類和父類都未實現該方法,而且沒有實現動態解析,那麼就會進入消息轉發階段
imp = (IMP)_objc_msgForward_impcache;
cache_fill(cls, sel, imp, inst);
done:
runtimeLock.unlock();
return imp;
}
複製代碼
以前在彙編代碼中就已經查找過緩存了,此處再查找一次緩存是爲了防止運行過程當中動態給currentClass增長方法。
_objc_msgForward_impcache實際上是一個存放在內存中的函數指針,爲彙編實現,內部會調用__objc_msgForward函數。
具體流程圖以下:
所以咱們能夠得出結論,objc_msgSend分爲3個步驟
在上文中lookUpImpOrForward函數用來進行方法查找,在函數中使用cache_getImp函數去類對象的緩存中查找方法。使用getMethodNoSuper_nolock函數去類對象的方法列表中去查詢,getMethodNoSuper_nolock源碼以下:
static method_t *
getMethodNoSuper_nolock(Class cls, SEL sel)
{
//遍歷方法列表,獲得method_list_t,經過search_method_list函數去method_list_t中查找對應方法
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;
}
複製代碼
由於類對象中的class_rw_t中方法列表是method_array_t類型的二維數組,內部存放着method_list_t,所以,getMethodNoSuper_nolock函數就是遍歷二維數組,而後拿到對應的method_list_t,最後調用search_method_list函數去method_list_t中查找對應的方法,源碼以下:
static method_t *search_method_list(const method_list_t *mlist, SEL sel)
{
int methodListIsFixedUp = mlist->isFixedUp();
int methodListHasExpectedSize = mlist->entsize() == sizeof(method_t);
//判斷當前method_list_t是否排好序
if (__builtin_expect(methodListIsFixedUp && methodListHasExpectedSize, 1)) {
//若是排好序,則使用二分查找,提升效率
return findMethodInSortedMethodList(sel, mlist);
} else {
// 若是沒有排序,則遍歷查詢
for (auto& meth : *mlist) {
if (meth.name == sel) return &meth;
}
}
return nil;
}
複製代碼
在search_method_list函數中會對method_list_t是否排好序進行判斷,若是method_list_t未排序,則經過遍歷去查詢方法列表。若是已排序,則會調用findMethodInSortedMethodList進行二分查找,提升查找效率,源碼以下:
static method_t *findMethodInSortedMethodList(SEL key, const method_list_t *list)
{
assert(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;
//count >>= 1表示count右移一位,而後賦值給count。至關於count = count / 2
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;
}
複製代碼
findMethodInSortedMethodList函數就是典型的二分查找。有興趣的同窗能夠自行去學習有關二分查找的更多知識。
在本文中,全部有關objc_msgSend的僞碼,可參考Hmmm, What's that Selector?
在OC中方法調用底層是經過調用objc_msgSend函數來執行方法。
[anObject doThings:things];
// 編譯以後
objc_msgSend(anObject, @selector(doThings:), things);
複製代碼
objc_msgSend的僞碼實現以下
id objc_msgSend(id self, SEL _cmd, ...) {
//經過self拿到對應的類對象
Class class = object_getClass(self);
//去類對象中查找對應的方法實現
IMP imp = class_getMethodImplementation(class, _cmd);
return imp ? imp(self, _cmd, ...) : 0;
}
複製代碼
其中class_getMethodImplementation函數就是方法查找的過程,具體的方法查找過程可參考上文,或者參考以前的文章Objective-C基礎之五(Runtime之Class結構解析)
若是在當前類和其父類中都沒有實現方法,那麼會進行動態方法解析,也就是上文中的resolveMethod函數,在此函數中能夠動態爲類增長方法。
//若是在當前類和父類中都沒有找到此方法,那麼,就進入動態方法解析階段
if (resolver && !triedResolver) {
runtimeLock.unlock();
resolveMethod(cls, sel, inst);
runtimeLock.lock();
//triedResolver代表當前動態方法解析只會執行一次
triedResolver = YES;
goto retry;
}
複製代碼
每次方法調用都會判斷是否進行過動態方法解析,若是沒有進行過動態方法解析,則會調用resolveMethod函數以下
static void resolveMethod(Class cls, SEL sel, id inst)
{
runtimeLock.assertUnlocked();
assert(cls->isRealized());
if (! cls->isMetaClass()) {
// try [cls resolveInstanceMethod:sel]
resolveInstanceMethod(cls, sel, inst);
}
else {
// try [nonMetaClass resolveClassMethod:sel]
// and [cls resolveInstanceMethod:sel]
resolveClassMethod(cls, sel, inst);
if (!lookUpImpOrNil(cls, sel, inst,
NO/*initialize*/, YES/*cache*/, NO/*resolver*/))
{
resolveInstanceMethod(cls, sel, inst);
}
}
}
複製代碼
若是當前類不是元類,那麼直接調用resolveInstanceMethod函數,在函數內部會查找類中是否實現了resolveInstanceMethod方法,若是實現了,則會調用resolveInstanceMethod方法。resolveInstanceMethod源碼以下
static void resolveInstanceMethod(Class cls, SEL sel, id inst)
{
runtimeLock.assertUnlocked();
assert(cls->isRealized());
//lookUpImpOrNil內部其實就是調用lookUpImpOrForward方法,去當前類及其父類中查找SEL_resolveInstanceMethod
if (! lookUpImpOrNil(cls->ISA(), SEL_resolveInstanceMethod, cls,
NO/*initialize*/, YES/*cache*/, NO/*resolver*/))
{
// Resolver not implemented.
return;
}
//經過objc_msgSend調用resolveInstanceMethod方法
BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
msg(cls, SEL_resolveInstanceMethod, sel);
}
複製代碼
上文中lookUpImpOrNil其實內部就是調用lookUpImpOrForward函數去查找SEL_resolveInstanceMethod方法,若是沒有找到,而且lookUpImpOrForward返回的imp==_objc_msgForward_impcache時,則返回nil。也就是判斷當前類以及它的父類是否實現了SEL_resolveInstanceMethod方法,若是未實現,則不執行下一步的objc_msgSend(cls, SEL_resolveInstanceMethod, sel)方法。
IMP lookUpImpOrNil(Class cls, SEL sel, id inst,
bool initialize, bool cache, bool resolver)
{
IMP imp = lookUpImpOrForward(cls, sel, inst, initialize, cache, resolver);
if (imp == _objc_msgForward_impcache) return nil;
else return imp;
}
複製代碼
若是當前類或者它的父類實現了SEL_resolveInstanceMethod方法,則調用objc_msgSend(cls, SEL_resolveInstanceMethod, sel)從新走一遍消息發送流程。目的就是找到SEL_resolveInstanceMethod方法而且進行調用。
lookUpImpOrForward只是返回方法的地址,具體方法的調用由彙編實現。
若是當前類是元類,那麼就會調用resolveClassMethod,具體內部實現和類對象的實現一致。不論是resolveInstanceMethod仍是resolveClassMethod,咱們均可以在這兩個方法中動態的爲類和元類增長方法。當執行過動態方法解析以後,會執行如下操做
//triedResolver代表當前動態方法解析只會執行一次
triedResolver = YES;
goto retry;
複製代碼
首先會將triedResolver標記設置爲YES,而後跳轉到retry標記處從新執行一次方法查找流程,若是咱們在resolveInstanceMethod或者resolveClassMethod方法中動態爲類新增了方法,那麼執行retry的時候會再次到類的緩存或者方法列表中查找到對應的方法而且執行。
@interface XLPerson : NSObject
- (void)testInstance;
+ (void)testClass;
@end
複製代碼
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '+[XLPerson test]: unrecognized selector sent to class 0x1000022d0'
複製代碼
@implementation XLPerson
- (void)personTestInstance{
NSLog(@"personTestInstance");
}
+ (void)personTestClass{
NSLog(@"personTestClass");
}
//對象方法消息轉發
+ (BOOL)resolveInstanceMethod:(SEL)sel{
if (sel == @selector(test)) {
Method method = class_getInstanceMethod(self, @selector(personTestInstance));
class_addMethod(self,
sel,
method_getImplementation(method),
method_getTypeEncoding(method));
return YES;
}
return [super resolveInstanceMethod:sel];
}
//類方法消息轉發
+ (BOOL)resolveClassMethod:(SEL)sel{
if (sel == @selector(test)) {
Method method = class_getClassMethod(self, @selector(personTestClass));
class_addMethod(object_getClass(self),
sel,
method_getImplementation(method),
method_getTypeEncoding(method));
return YES;
}
return [super resolveClassMethod:sel];
}
@end
複製代碼
2020-01-13 10:10:09.364022+0800 Test[74806:8439331] personTestInstance
2020-01-13 10:10:12.285118+0800 Test[74806:8439331] personTestClass
複製代碼
動態方法解析的流程圖以下
若是咱們沒有在消息轉發的時候動態爲類或者元類增長方法,那麼,就會來到最後一步,就是消息轉發階段。消息轉發過程由於是不開源的,因此咱們先從Demo入手來看一下消息轉發的流程。
@interface XLTeacher : NSObject
- (void)testInstance;
+ (void)testClass;
@end
@implementation XLTeacher
- (void)testInstance{
NSLog(@"%s", __func__);
}
+ (void)testClass{
NSLog(@"%s", __func__);
}
@end
複製代碼
@implementation XLPerson
//實例對象方法的轉發,須要返回一個實例對象
- (id)forwardingTargetForSelector:(SEL)aSelector{
if (aSelector == @selector(testInstance)) {
return [[XLTeacher alloc] init];
}
return [super forwardingTargetForSelector:aSelector];
}
//類對象方法的轉發,須要返回一個類對象
+ (id)forwardingTargetForSelector:(SEL)aSelector{
if (aSelector == @selector(testClass)) {
return [XLTeacher class];
}
return [super forwardingTargetForSelector:aSelector];
}
@end
複製代碼
在main函數中執行XLPerson的方法testInstance和testClass,會發現最後執行了XLTeacher中的同名方法testInstance和testClass
@implementation XLPerson
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
if (aSelector == @selector(testInstance)) {
return [NSMethodSignature signatureWithObjCTypes:"v@:"];
}
return [super methodSignatureForSelector:aSelector];
}
+ (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
if (aSelector == @selector(testClass)) {
return [NSMethodSignature signatureWithObjCTypes:"v@:"];
}
return [super methodSignatureForSelector:aSelector];
}
@end
複製代碼
@implementation XLPerson
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
if (aSelector == @selector(testInstance)) {
return [NSMethodSignature signatureWithObjCTypes:"v@:"];
}
return [super methodSignatureForSelector:aSelector];
}
+ (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
if (aSelector == @selector(testClass)) {
return [NSMethodSignature signatureWithObjCTypes:"v@:"];
}
return [super methodSignatureForSelector:aSelector];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation{
[anInvocation invokeWithTarget:[[XLTeacher alloc] init]];
}
+ (void)forwardInvocation:(NSInvocation *)anInvocation{
[anInvocation invokeWithTarget:[XLTeacher class]];
}
@end
複製代碼
- 因爲methodSignatureForSelector已經返回了正確的方法簽名,因此在forwardInvocation方法中就不須要額外設置方法簽名了。而且須要保證方法簽名和咱們須要調用的方法簽名保持一致。
- 而且以上methodSignatureForSelector方法和forwardInvocation方法都有對應的實例方法和類方法。實例方法須要返回一個實例對象,類方法須要返回類對象。
首先上文中說到,若是以上三個階段都沒有找到方法的話,程序會報錯,報錯信息以下:
2020-01-13 11:42:58.563144+0800 Test[89986:8560251] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[XLPerson testInstance]: unrecognized selector sent to instance 0x102856690'
*** First throw call stack:
(
0 CoreFoundation 0x00007fff37632f53 __exceptionPreprocess + 250
1 libobjc.A.dylib 0x00007fff6d6f8835 objc_exception_throw + 48
2 CoreFoundation 0x00007fff376bd106 -[NSObject(NSObject) __retain_OA] + 0
3 CoreFoundation 0x00007fff375d96cb ___forwarding___ + 1427
4 CoreFoundation 0x00007fff375d90a8 _CF_forwarding_prep_0 + 120
5 Test 0x0000000100000d9b main + 91
6 libdyld.dylib 0x00007fff6ea5b2e5 start + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException
複製代碼
在報錯堆棧中,明顯能夠看出,執行消息轉發是經過___forwarding___來完成的,可是咱們在源碼中是找不到___forwarding___的實現的,由於對於消息轉發這部分的源碼並不開源,所以咱們能夠藉助上文提到的文章Hmmm, What's that Selector?來研究___forwarding___實現過程。
void __forwarding__(BOOL isStret, void *frameStackPointer, ...) {
//經過堆棧指針frameStackPointer獲取當前接收者
id receiver = *(id *)frameStackPointer;
//經過frameStackPointer+4獲取到方法選擇器
SEL sel = *(SEL *)(frameStackPointer + 4);
//獲取到receiver對應的類對象
Class receiverClass = object_getClass(receiver);
//若是receiverClass實現了forwardingTargetForSelector
if (class_respondsToSelector(receiverClass, @selector(forwardingTargetForSelector:))) {
//調用receiver的forwardingTargetForSelector方法,獲取到須要轉發的target
id forwardingTarget = [receiver forwardingTargetForSelector:sel];
if (forwardingTarget) {
//而後經過調用objc_msgSend函數從新執行消息發送流程,將forwardingTarget做爲第一個參數
return objc_msgSend(forwardingTarget, sel, ...);
}
}
//若是對象已經被釋放,則打印錯誤信息
const char *className = class_getName(object_getClass(receiver));
const char *zombiePrefix = "_NSZombie_";
size_t prefixLen = strlen(zombiePrefix);
if (strncmp(className, zombiePrefix, prefixLen) == 0) {
CFLog(kCFLogLevelError,
@"-[%s %s]: message sent to deallocated instance %p",
className + prefixLen,
sel_getName(sel),
receiver);
<breakpoint-interrupt>
}
//若是receiverClass實現了methodSignatureForSelector方法
if (class_respondsToSelector(receiverClass, @selector(methodSignatureForSelector:))) {
//調用receiver的methodSignatureForSelector獲取到對應的方法簽名
NSMethodSignature *methodSignature = [receiver methodSignatureForSelector:sel];
if (methodSignature) {
//判斷methodSignature是不是結構體,若是不是,則打印錯誤信息
BOOL signatureIsStret = [methodSignature _frameDescriptor]->returnArgInfo.flags.isStruct;
if (signatureIsStret != isStret) {
CFLog(kCFLogLevelWarning ,
@"*** NSForwarding: warning: method signature and compiler disagree on struct-return-edness of '%s'. Signature thinks it does%s return a struct, and compiler thinks it does%s.",
sel_getName(sel),
signatureIsStret ? "" : not,
isStret ? "" : not);
}
//若是receiverClass實現了forwardInvocation方法
if (class_respondsToSelector(receiverClass, @selector(forwardInvocation:))) {
//根據方法簽名和堆棧指針獲取到對應的invocation
NSInvocation *invocation = [NSInvocation _invocationWithMethodSignature:methodSignature
frame:frameStackPointer];
//調用receiver的forwardInvocation方法,將剛剛生成的invocation傳遞過去
[receiver forwardInvocation:invocation];
void *returnValue = NULL;
//獲取到invocation中的返回值
[invocation getReturnValue:&value];
return returnValue;
} else {
CFLog(kCFLogLevelWarning ,
@"*** NSForwarding: warning: object %p of class '%s' does not implement forwardInvocation: -- dropping message",
receiver,
className);
return 0;
}
}
}
const char *selName = sel_getName(sel);
SEL *registeredSel = sel_getUid(selName);
//若是消息轉發都失敗的話走如下錯誤判斷
if (sel != registeredSel) {
//若是當前的sel和sel_getUid(selName)獲取到的sel不一致
CFLog(kCFLogLevelWarning ,
@"*** NSForwarding: warning: selector (%p) for message '%s' does not match selector known to Objective C runtime (%p)-- abort",
sel,
selName,
registeredSel);
} else if (class_respondsToSelector(receiverClass, @selector(doesNotRecognizeSelector:))) {
//若是當前receiverClass實現了doesNotRecognizeSelector方法,則調用此方法
[receiver doesNotRecognizeSelector:sel];
} else {
//未實現doesNotRecognizeSelector的方法的日誌
CFLog(kCFLogLevelWarning ,
@"*** NSForwarding: warning: object %p of class '%s' does not implement doesNotRecognizeSelector: -- abort",
receiver,
className);
}
// 殺死進程
kill(getpid(), 9);
}
複製代碼
由上述僞代碼,能夠獲得消息轉發的總體流程
以上就是objc_msgSend的完整流程,大致能夠分爲3個階段,消息發送、動態方法解析和消息轉發
消息轉發流程簡圖以下:
以上內容純屬我的理解,若是有什麼不對的地方歡迎留言指正。
一塊兒學習,一塊兒進步~~~