在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个阶段,消息发送、动态方法解析和消息转发
消息转发流程简图以下:
以上内容纯属我的理解,若是有什么不对的地方欢迎留言指正。
一块儿学习,一块儿进步~~~