
消息轉(zhuǎn)發(fā)流程
目錄
- 一、消息機(jī)制
- 二、方法調(diào)用源碼查看
- 三、objc_msgSend的執(zhí)行流程
- 四、番外篇
一、簡(jiǎn)要說明OC消息機(jī)制(objc_msgSend執(zhí)行流程)
OC中的方法調(diào)用,其實(shí)都是轉(zhuǎn)換為objc_msgSend函數(shù)調(diào)用
objc_msgSend的執(zhí)行流程可以分為3大階段
- 消息發(fā)送
- 動(dòng)態(tài)方法解析
- 消息轉(zhuǎn)發(fā)
如果找不到合適的方法調(diào)用,會(huì)報(bào)錯(cuò) unrecognized selector sent to instance 0x100555ad0
二、方法調(diào)用源碼查看
int main(int argc, const char * argv[]) {
@autoreleasepool {
// 創(chuàng)建消息接收者(receiver)
MJPerson *person = [[MJPerson alloc] init];
// 對(duì)象方法調(diào)用
[person personTest];
// 類方法調(diào)用
[MJPerson initialize];
}
return 0;
}
和以前一樣,我們使用clang看一下源碼
$ xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m
- 2.1、對(duì)象方法調(diào)用 [person personTest];
// C++源碼
((void (*)(id, SEL))(void *)objc_msgSend)((id)person, sel_registerName("personTest"));
// 刪除強(qiáng)制轉(zhuǎn)換后
objc_msgSend(person, sel_registerName("personTest"));
// sel_registerName 相當(dāng)于 @selector,所以,最終我們可以寫成
objc_msgSend(person, @selector(personTest));
- 2.2、類方法調(diào)用[MJPerson initialize];
// C++源碼
((void (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("MJPerson"), sel_registerName("initialize"));
// 刪除強(qiáng)制轉(zhuǎn)換后
objc_msgSend(objc_getClass("MJPerson"), sel_registerName("initialize"));
// 同理,最終我們可以寫成
objc_msgSend([MJPerson class], @selector(initialize))
三、objc_msgSend的執(zhí)行流程
- 3.1、消息發(fā)送階段

李明杰-底層原理-消息發(fā)送流程.png
objc-runtime-new.mm類中的lookUpImpOrForward方法我們可以看到其實(shí)現(xiàn)
// Try this class's cache.
// 從當(dāng)前類緩存中查找
imp = cache_getImp(cls, sel);
if (imp) goto done;
// Try this class's method lists.
// 從當(dāng)前類方法列表中查找
{
Method meth = getMethodNoSuper_nolock(cls, sel);
if (meth) {
// 如果找到,就將它加入類的緩存中
log_and_fill_cache(cls, meth->imp, sel, inst, cls);
imp = meth->imp;
goto done;
}
}
// Try superclass caches and method lists.
// 從父類的緩存中查找
{
unsigned attempts = unreasonableClassCount();
// 一層一層向上查找(superClass->superSuperClass->...)
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;
}
}
}
- 3.2、動(dòng)態(tài)方法解析階段

動(dòng)態(tài)方法解析
// No implementation found. Try method resolver once.
// 判斷是否進(jìn)行過動(dòng)態(tài)方法解析,如果沒有解析過,則執(zhí)行括號(hào)中的內(nèi)容
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.
// 第一次解析后標(biāo)記,第二次就不會(huì)進(jìn)行解析了
triedResolver = YES;
// 回到消息發(fā)送階段
goto retry;
}
// No implementation found, and method resolver didn't help.
// Use forwarding.
// 消息轉(zhuǎn)發(fā)階段
imp = (IMP)_objc_msgForward_impcache;
cache_fill(cls, sel, imp, inst);
done:
runtimeLock.unlockRead();
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);
}
}
}
- 3.2.1、動(dòng)態(tài)方法解析具體實(shí)現(xiàn)一:對(duì)象方法調(diào)用解析
@interface MJPerson : NSObject
- (void)test;
@end
#import "MJPerson.h"
#import <objc/runtime.h>
@implementation MJPerson
//- (void)test {
// NSLog(@"%s", __func__);
//}
- (void)other {
NSLog(@"%s", __func__);
}
+ (BOOL)resolveInstanceMethod:(SEL)sel {
if (sel == @selector(test)) {
// 獲取其他方法
Method otherThod = class_getInstanceMethod(self, @selector(other));
// 動(dòng)態(tài)添加test方法實(shí)現(xiàn)
class_addMethod(self,
sel,
method_getImplementation(otherThod),
method_getTypeEncoding(otherThod));
// 返回yes代表有動(dòng)態(tài)添加方法
return YES;
}
return [super resolveInstanceMethod:sel];
}
@end
#import <Foundation/Foundation.h>
#import "MJPerson.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
MJPerson *person = [[MJPerson alloc] init];
[person test];
}
return 0;
}
2019-09-30 19:45:46.065352+0800 Test12[18214:1032252] -[MJPerson other]
- 3.2.2、類方法調(diào)用解析
@interface MJPerson : NSObject
+ (void)test;
@end
#import "MJPerson.h"
#import <objc/runtime.h>
@implementation MJPerson
//+ (void)test {
// NSLog(@"%s", __func__);
//}
void c_other(id self, SEL _cmd) {
NSLog(@"c_other -%@ -%@", self, NSStringFromSelector(_cmd));
}
+ (BOOL)resolveClassMethod:(SEL)sel {
if (sel == @selector(test)) {
// 第一個(gè)參數(shù)是 object_getClass(self)
class_addMethod(object_getClass(self), sel, (IMP)c_other, "v16@0:8");
return YES;
}
return [super resolveInstanceMethod:sel];
}
@end
#import <Foundation/Foundation.h>
#import "MJPerson.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
[MJPerson test];
}
return 0;
}
2019-09-30 19:48:41.825602+0800 Test12[18258:1034184] c_other -MJPerson -test
- 3.2.3、C語言函數(shù)動(dòng)態(tài)解析
#import "MJPerson.h"
#import <objc/runtime.h>
@implementation MJPerson
//- (void)test {
// NSLog(@"%s", __func__);
//}
void c_other(id self, SEL _cmd) {
NSLog(@"c_other -%@ -%@", self, NSStringFromSelector(_cmd));
}
+ (BOOL)resolveInstanceMethod:(SEL)sel {
if (sel == @selector(test)) {
// 獲取其他方法
class_addMethod(self, sel, (IMP)c_other, "v16@0:8");
// 返回yes代表有動(dòng)態(tài)添加方法
return YES;
}
return [super resolveInstanceMethod:sel];
}
@end
- 3.3、消息轉(zhuǎn)發(fā)

消息轉(zhuǎn)發(fā)流程
- 3.3.1、消息轉(zhuǎn)發(fā)(一):forwardingTargetForSelector(MJPerson 消息由 MJStudent處理)
@interface MJPerson : NSObject
- (void)test;
@end
@implementation MJPerson
- (id)forwardingTargetForSelector:(SEL)aSelector {
if (aSelector == @selector(test)) {
// objc_msgSend(student, aSelector);
return [[MJStudent alloc] init];
}
return [super forwardingTargetForSelector:aSelector];
}
@end
#import "MJStudent.h"
@implementation MJStudent
- (void)test {
NSLog(@"%s", __func__);
}
@end
#import <Foundation/Foundation.h>
#import "MJPerson.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
MJPerson *person = [[MJPerson alloc] init];
[person test];
}
return 0;
}
2019-09-30 19:59:13.819509+0800 Test12[18418:1039881] -[MJStudent test]
- 3.3.2、消息轉(zhuǎn)發(fā)(二):forwardInvocation
MJPerson 的消息 forwardingTargetForSelector方法未能處理,進(jìn)入下一步轉(zhuǎn)發(fā)流程
@interface MJPerson : NSObject
- (void)test;
@end
#import "MJPerson.h"
#import "MJStudent.h"
@implementation MJPerson
- (id)forwardingTargetForSelector:(SEL)aSelector {
if (aSelector == @selector(test)) {
return nil;
}
return [super forwardingTargetForSelector:aSelector];
}
// 方法簽名:返回值、參數(shù)類型
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
if (aSelector == @selector(test)) {
return [NSMethodSignature signatureWithObjCTypes:"v16@0:8"];
}
return [super methodSignatureForSelector:aSelector];
}
//
/**
NSInvocation 封裝了一個(gè)方法調(diào)用,包括方法調(diào)用者、方法名、方法參數(shù)
anInvocation.target 方法調(diào)用者
anInvocation.selector 方法名
[anInvocation getArgument:NULL atIndex:0] 方法參數(shù)
*/
- (void)forwardInvocation:(NSInvocation *)anInvocation {
/**
方法一
anInvocation.target = [[MJStudent alloc] init];
[anInvocation invoke];
*/
// 方法二
[anInvocation invokeWithTarget:[[MJStudent alloc] init]];
}
@end
#import "MJStudent.h"
@implementation MJStudent
- (void)test {
NSLog(@"%s", __func__);
}
@end
#import <Foundation/Foundation.h>
#import "MJPerson.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
MJPerson *person = [[MJPerson alloc] init];
[person test];
}
return 0;
}
2019-09-30 20:08:02.735260+0800 Test12[18511:1043993] -[MJStudent test]
四、番外篇
- 4.1、類方法轉(zhuǎn)發(fā)到對(duì)象方法
#import "MJStudent.h"
@implementation MJStudent
+ (void)test {
NSLog(@"%s", __func__);
}
- (void)test {
NSLog(@"%s", __func__);
}
@end
#import "MJPerson.h"
#import "MJStudent.h"
@implementation MJPerson
+ (id)forwardingTargetForSelector:(SEL)aSelector {
if (aSelector == @selector(test)) {
/**
objc_msgSend([[MJStudent alloc] init], @selector(test));
[[[MJStudent alloc] init] test]
*/
return [[MJStudent alloc] init];
}
return [super forwardingTargetForSelector:aSelector];
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
[MJPerson test];
}
return 0;
}
2019-09-05 21:05:50.180158+0800 test[40754:500893] -[MJStudent test]
- 4.2、動(dòng)態(tài)實(shí)現(xiàn) set、get方法
@interface MJPerson : NSObject
@property (nonatomic, assign) int age;
@end
#import "MJPerson.h"
#import <objc/runtime.h>
@implementation MJPerson
@dynamic age;
void setAge(id self, SEL _cmd, int age) {
NSLog(@"age is %d", age);
}
int age(id self, SEL _cmd) {
return 120;
}
+ (BOOL)resolveInstanceMethod:(SEL)sel {
if (sel == @selector(setAge:)) {
class_addMethod(self, sel, (IMP)setAge, "v@:I");
return YES;
} else if (sel == @selector(age)) {
class_addMethod(self, sel, (IMP)age, "i@:I");
}
return [super resolveInstanceMethod:sel];
}
@end
#import <Foundation/Foundation.h>
#import "MJPerson.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
MJPerson *person = [[MJPerson alloc] init];
person.age = 20;
NSLog(@"person.age is %d", person.age);
}
return 0;
}
2019-09-07 18:40:20.740030+0800 test[1514:27406] age is 20
2019-09-07 18:40:20.740276+0800 test[1514:27406] person.age is 120