源碼
message.h
message匯編源碼
runtime源碼
在iOS中手形,方法調(diào)用過(guò)程分三步
消息發(fā)送:從類及父類的緩存列表以及方法列表查找方法库糠。
動(dòng)態(tài)解析:如果消息發(fā)送階段沒(méi)有找到方法,則會(huì)進(jìn)入動(dòng)態(tài)解析階段瞬欧,負(fù)責(zé)動(dòng)態(tài)的添加方法實(shí)現(xiàn)。
消息轉(zhuǎn)發(fā):如果也沒(méi)有實(shí)現(xiàn)動(dòng)態(tài)解析方法唉侄,則會(huì)進(jìn)行消息轉(zhuǎn)發(fā)階段属划,將消息轉(zhuǎn)發(fā)給可以處理消息的接受者來(lái)處理贬墩。
如果消息轉(zhuǎn)發(fā)也沒(méi)有實(shí)現(xiàn),則會(huì)拋出常見的異常unrecognzied selector sent to instance
我們先來(lái)去掉方法調(diào)用的偽裝,在main.m文件中創(chuàng)建一個(gè)Animal
類唠粥,添加一個(gè)run
實(shí)例方法
在main函數(shù)中
int main(int argc, char * argv[]) {
Animal *animal = [[Animal alloc] init];
[animal run];
return 0;
}
通過(guò)xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m
命令轉(zhuǎn)成C++代碼main.cpp
文件晤愧,進(jìn)入文件后定位到main函數(shù)可以看到
int main(int argc, char * argv[]) {
Animal *animal = ((Animal *(*)(id, SEL))(void *)objc_msgSend)((id)((Animal *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("Animal"), sel_registerName("alloc")), sel_registerName("init"));
((void (*)(id, SEL))(void *)objc_msgSend)((id)animal, sel_registerName("run"));
return 0;
}
通過(guò)上面源碼可以很清楚的看到C++底層將方法調(diào)用最終轉(zhuǎn)換為調(diào)用objc_msgSend
,并且傳入?yún)?shù)(id)animal
(消息接受者self
),sel_registerName("run")
(消息名_cmd
)舅巷,因此OC的方法調(diào)用也稱為消息機(jī)制钠右,表示給方法調(diào)用者發(fā)送消息飒房。
接下來(lái)我們慢慢剖析一個(gè)消息發(fā)送的完整過(guò)程狠毯。
Step 1.1垃你、 消息發(fā)送-緩存列表查找
Objective-C中方法調(diào)用從objc_msgSend
開始就正式開始,objc_msgSend
的申明定義在源碼message.h
中少辣,實(shí)現(xiàn)是在Messengers.subproj/
這個(gè)匯編文件夾中锨亏,以arm64的匯編源碼為例(這里就只貼關(guān)鍵的代碼)
_objc_msgSend
,對(duì)消息接受者判空,是則進(jìn)入LReturnZero
返回nil反浓,否則進(jìn)入CacheLookup
檢查緩存
ENTRY _objc_msgSend
...
#if SUPPORT_TAGGED_POINTERS
b.le LNilOrTagged // 判空
#else
b.eq LReturnZero // 不為空則進(jìn)入LGetIsaDone
#endif
......
LGetIsaDone:
CacheLookup NORMAL // 開始查找
......
CacheLookup
開始查找緩存,查到緩存進(jìn)入CacheHit
并返回imp,否則進(jìn)入CheckMiss
.macro CacheLookup
......
CacheHit $0
......
CheckMiss
.endmacro
CheckMiss
根據(jù)傳入?yún)?shù)跳轉(zhuǎn)到__objc_msgSend_uncached
or __objc_msgLookup_uncached
.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
__objc_msgSend_uncached
調(diào)用MethodTableLookup
進(jìn)入方法列表查找
STATIC_ENTRY __objc_msgSend_uncached
UNWIND __objc_msgSend_uncached, FrameWithNoSaves
MethodTableLookup // 這里是MethodTableLookup
TailCallFunctionPointer x17
END_ENTRY __objc_msgSend_uncached
MethodTableLookup
內(nèi)部邏輯進(jìn)入方法列表查找的核心函數(shù)__class_lookupMethodAndLoadCache3
,該方法在runtime.m
文件中實(shí)現(xiàn)
.macro MethodTableLookup
......
bl __class_lookupMethodAndLoadCache3
......
.endmacro
進(jìn)入到__class_lookupMethodAndLoadCache3
后我們接下來(lái)看1.2的方法列表查找過(guò)程。
Step 1.2、 消息發(fā)送-方法列表查找
從核心函數(shù)_class_lookupMethodAndLoadCache3
開始
IMP _class_lookupMethodAndLoadCache3(id obj, SEL sel, Class cls)
{
return lookUpImpOrForward(cls, sel, obj,
YES/*initialize*/, NO/*cache*/, YES/*resolver*/);
}
IMP lookUpImpOrForward(Class cls, SEL sel, id inst,
bool initialize, bool cache, bool resolver)
{
IMP imp = nil;
bool triedResolver = NO;
runtimeLock.assertUnlocked();
// 已經(jīng)在1.1的緩存中已經(jīng)查找過(guò)缔俄,所以傳入的NO不再進(jìn)行緩存查找
if (cache) {
imp = cache_getImp(cls, sel);
if (imp) return imp;
}
runtimeLock.lock();
checkIsKnownClass(cls);
if (!cls->isRealized()) {
cls = realizeClassMaybeSwiftAndLeaveLocked(cls, runtimeLock);
}
if (initialize && !cls->isInitialized()) {
cls = initializeAndLeaveLocked(cls, inst, runtimeLock);
// runtimeLock may have been dropped but is now locked again
// If sel == initialize, class_initialize will send +initialize and
// then the messenger will send +initialize again after this
// procedure finishes. Of course, if this is not being called
// from the messenger then it won't happen. 2778172
}
retry:
runtimeLock.assertLocked();
// 嘗試查找一次緩存蟹略,如果找到直接返回
imp = cache_getImp(cls, sel);
if (imp) goto done;
// 傳入對(duì)象和方法名状婶,在方法列表中獲取
{
Method meth = getMethodNoSuper_nolock(cls, sel);
if (meth) {
// 查找到后進(jìn)行緩存草姻,并直接返回
log_and_fill_cache(cls, meth->imp, sel, inst, cls);
imp = meth->imp;
goto done;
}
}
// 依次遍歷父類的方法列表中或緩存進(jìn)行查找
{
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) {
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;
}
}
// 父類的方法列表
Method meth = getMethodNoSuper_nolock(curClass, sel);
if (meth) {
log_and_fill_cache(cls, meth->imp, sel, inst, curClass);
imp = meth->imp;
goto done;
}
}
}
// 未找到imp跌榔,如果傳入resolver為YES且triedResolver為NO(未解析過(guò))則開始動(dòng)態(tài)解析過(guò)程
if (resolver && !triedResolver) {
runtimeLock.unlock();
// 開始動(dòng)態(tài)解析
resolveMethod(cls, sel, inst);
runtimeLock.lock();
// Don't cache the result; we don't hold the lock so it may have
// changed already. Re-do the search from scratch instead.
triedResolver = YES;
goto retry;
}
// 未找到imp且動(dòng)態(tài)解析也未解析到,則開始消息轉(zhuǎn)發(fā)
imp = (IMP)_objc_msgForward_impcache;
cache_fill(cls, sel, imp, inst);
done:
runtimeLock.unlock();
return imp;
}
realizeClass
是初始化了很多數(shù)據(jù)示绊,包括cls->ro
賦值給cls->rw
取胎,添加元類version為7,cls->chooseClassArrayIndex()
設(shè)置cls
的索引匪傍,supercls = realizeClass(remapClass(cls->superclass)); metacls = realizeClass(remapClass(cls->ISA()))
初始化superclass
和cls->isa
,后邊針對(duì)沒(méi)有優(yōu)化的結(jié)構(gòu)進(jìn)行賦值這里不多講薪棒,然后協(xié)調(diào)實(shí)例變量偏移布局棵介,設(shè)置cls->setInstanceSize
,拷貝flags
從ro
到rw
中扣蜻,然后添加subclass
和rootclass
,最后添加類別的方法芳肌,協(xié)議亿笤,和屬性汪榔。
getMethodNoSuper_nolock
在類對(duì)象的方法列表中查找
getMethodNoSuper_nolock(Class cls, SEL sel)
{
runtimeLock.assertLocked();
assert(cls->isRealized());
// cls->data() 得到的是 class_rw_t
// class_rw_t->methods 得到的是methods二維數(shù)組
for (auto mlists = cls->data()->methods.beginLists(),
end = cls->data()->methods.endLists();
mlists != end;
++mlists)
{
// mlists 為 method_list_t
method_t *m = search_method_list(*mlists, sel);
if (m) return m;
}
return nil;
}
search_method_list
static method_t *search_method_list(const method_list_t *mlist, SEL sel)
{
int methodListIsFixedUp = mlist->isFixedUp();
int methodListHasExpectedSize = mlist->entsize() == sizeof(method_t);
// 如果方法列表是有序的,則使用效率較高的二分法查找方法
if (__builtin_expect(methodListIsFixedUp && methodListHasExpectedSize, 1)) {
return findMethodInSortedMethodList(sel, mlist);
} else {
// 否則遍歷列表查找
for (auto& meth : *mlist) {
if (meth.name == sel) return &meth;
}
}
return nil;
}
findMethodInSortedMethodList
函數(shù)內(nèi)二分查找實(shí)現(xiàn)原理
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;
// >>1 表示將變量n的各個(gè)二進(jìn)制位順序右移1位剥悟,最高位補(bǔ)二進(jìn)制0躏尉。
// count >>= 1 如果count為偶數(shù)則值變?yōu)?count / 2)教藻。如果count為奇數(shù)則值變?yōu)?count-1) / 2
for (count = list->count; count != 0; count >>= 1) {
// probe 指向數(shù)組中間的值
probe = base + (count >> 1);
// 取出中間method_t的name悄窃,也就是SEL
uintptr_t probeValue = (uintptr_t)probe->name;
if (keyValue == probeValue) {
// 取出 probe
while (probe > first && keyValue == (uintptr_t)probe[-1].name) {
probe--;
}
// 返回方法
return (method_t *)probe;
}
// 如果keyValue > probeValue 則折半向后查詢
if (keyValue > probeValue) {
base = probe + 1;
count--;
}
}
return nil;
}
Step 2纠炮、 動(dòng)態(tài)解析
動(dòng)態(tài)解析階段源碼:
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
函數(shù)
static void resolveInstanceMethod(Class cls, SEL sel, id inst)
{
runtimeLock.assertUnlocked();
assert(cls->isRealized());
if (! lookUpImpOrNil(cls->ISA(), SEL_resolveInstanceMethod, cls,
NO/*initialize*/, YES/*cache*/, NO/*resolver*/))
{
// 未實(shí)現(xiàn)動(dòng)態(tài)解析方法SEL_resolveInstanceMethod
return;
}
// 調(diào)用動(dòng)態(tài)解析方法也就是OC中的類方法"resolveInstanceMethod:"
BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
bool resolved = msg(cls, SEL_resolveInstanceMethod, sel);
// 動(dòng)態(tài)解析后再次進(jìn)行查找
IMP imp = lookUpImpOrNil(cls, sel, inst,
NO/*initialize*/, YES/*cache*/, NO/*resolver*/);
if (resolved && PrintResolving) {
if (imp) {
_objc_inform("RESOLVE: method %c[%s %s] "
"dynamically resolved to %p",
cls->isMetaClass() ? '+' : '-',
cls->nameForLogging(), sel_getName(sel), imp);
}
else {
// Method resolver didn't add anything?
_objc_inform("RESOLVE: +[%s resolveInstanceMethod:%s] returned YES"
", but no new implementation of %c[%s %s] was found",
cls->nameForLogging(), sel_getName(sel),
cls->isMetaClass() ? '+' : '-',
cls->nameForLogging(), sel_getName(sel));
}
}
}
lookUpImpOrNil
IMP lookUpImpOrNil(Class cls, SEL sel, id inst,
bool initialize, bool cache, bool resolver)
{
IMP imp = lookUpImpOrForward(cls, sel, inst, initialize, cache, resolver);
// 如果是`_objc_msgForward_impcache`則說(shuō)明穷躁,動(dòng)態(tài)解析添加失敗耕肩,返回nil
if (imp == _objc_msgForward_impcache) return nil;
else return imp;
}
在進(jìn)行過(guò)一次動(dòng)態(tài)解析后,通過(guò)resolver
和triedResolver
兩個(gè)參數(shù)的值知道折砸,無(wú)論動(dòng)態(tài)解析是否有用看疗,都不會(huì)在lookUpImpOrForward
中再次進(jìn)行動(dòng)態(tài)解析。
接下來(lái)我們看一下OC中動(dòng)態(tài)解析的方法實(shí)現(xiàn)以及怎么實(shí)現(xiàn)動(dòng)態(tài)解析
動(dòng)態(tài)解析對(duì)象方法+(BOOL)resolveInstanceMethod:(SEL)sel
睦授,動(dòng)態(tài)解析類方法+(BOOL)resolveClassMethod:(SEL)sel
两芳。
// 這里以`resolveInstanceMethod :`舉例怎么實(shí)現(xiàn)動(dòng)態(tài)解析
+ (BOOL)resolveInstanceMethod:(SEL)sel {
if (sel == @selector(test)) {
// 獲取實(shí)例對(duì)象方法的指針
Method otherMethod = class_getInstanceMethod(self, @selector(run));
// 添加到類實(shí)例對(duì)象方法列表
class_addMethod(self, sel, method_getImplementation(otherMethod), method_getTypeEncoding(otherMethod));
// 是否動(dòng)態(tài)添加
return YES;
}
NSLog(@"%s", __func__);
return [super resolveInstanceMethod:sel];
}
int main(int argc, char * argv[]) {
Animal *animal = [[Animal alloc] init];
[animal run];
[animal performSelector:@selector(test)];
return 0;
}
animal
對(duì)象調(diào)用方法test
,可以看到最后調(diào)的run
函數(shù)去枷,通過(guò)上面對(duì)消息發(fā)送的分析我們知道怖辆,當(dāng)本類和父類cache
和class_rw_t
中都找不到方法時(shí),就會(huì)進(jìn)行動(dòng)態(tài)解析的方法删顶,也就是說(shuō)會(huì)自動(dòng)調(diào)用類的resolveInstanceMethod:
方法進(jìn)行動(dòng)態(tài)查找竖螃。
需要注意
Method
是objc_method
結(jié)構(gòu)體,其內(nèi)部結(jié)構(gòu)同method_t
(內(nèi)部包含SEL
逗余、type
特咆、IMP
)結(jié)構(gòu)體相同,可以等同轉(zhuǎn)換
Step 3录粱、 消息轉(zhuǎn)發(fā)
由runtime
源碼中可以看出腻格,在未進(jìn)行動(dòng)態(tài)解析或者動(dòng)態(tài)解析失敗后,就會(huì)執(zhí)行imp = (IMP)_objc_msgForward_impcache;
進(jìn)行消息轉(zhuǎn)發(fā)啥繁,但是此部分代碼并未開源菜职,我們可以從Objectiv-C提供的模板方法來(lái)進(jìn)行分析。
在Objectiv-C中消息轉(zhuǎn)發(fā)涉及三個(gè)函數(shù):
1.- (id)forwardingTargetForSelector:(SEL)aSelector
2.- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
- (void)forwardInvocation:(NSInvocation *)anInvocation
如果forwardingTargetForSelector函數(shù)返回為nil或者沒(méi)有實(shí)現(xiàn)的話旗闽,就會(huì)調(diào)用methodSignatureForSelector方法酬核,用來(lái)返回一個(gè)方法簽名,這也是我們正確跳轉(zhuǎn)方法的最后機(jī)會(huì)适室。
如果methodSignatureForSelector方法返回正確的方法簽名就會(huì)調(diào)用forwardInvocation方法巾腕,forwardInvocation方法內(nèi)提供一個(gè)NSInvocation類型的參數(shù)您机,NSInvocation封裝了一個(gè)方法的調(diào)用咽安,包括方法的調(diào)用者船殉,方法名章母,以及方法的參數(shù)但狭。在forwardInvocation函數(shù)內(nèi)修改方法調(diào)用對(duì)象即可复隆。
如果methodSignatureForSelector返回的為nil迫吐,就會(huì)來(lái)到doseNotRecognizeSelector:方法內(nèi)部,程序crash提示無(wú)法識(shí)別選擇器unrecognized selector sent to instance整袁。
// 3.消息轉(zhuǎn)發(fā)階段(未進(jìn)行動(dòng)態(tài)解析或者動(dòng)態(tài)解析失敗進(jìn)入本階段)
- (id)forwardingTargetForSelector:(SEL)aSelector {
if (aSelector == @selector(roar)) {
return [[Lion alloc] init];
}
return [super forwardingTargetForSelector:aSelector];
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
if (aSelector == @selector(hunt)) {
// 創(chuàng)建方法簽名
// return [NSMethodSignature signatureWithObjCTypes:"v16@0:8"];
return [[[Lion alloc] init] methodSignatureForSelector:aSelector];
}
return [super methodSignatureForSelector:aSelector];
}
/// 如果上一步‘methodSignatureForSelector:’返回正確的方法簽名菠齿,則執(zhí)行此步。這里需要注意修改NSInvocation的target對(duì)象坐昙,由于修改前的對(duì)象是原始調(diào)用對(duì)象未實(shí)現(xiàn)該方法绳匀,所以一直進(jìn)行消息轉(zhuǎn)發(fā),造成調(diào)用棧溢出
/// @param anInvocation anInvocation炸客,封裝了上一步分方法簽名
- (void)forwardInvocation:(NSInvocation *)anInvocation {
NSLog(@"NSInvocation original target is %@", anInvocation.target);
Lion *target = [[Lion alloc] init];
anInvocation.target = target;
[anInvocation invoke];
// [anInvocation invokeWithTarget:[[Lion alloc] init]];
NSLog(@"NSInvocation destination target is %@", anInvocation.target);
}
同樣上述代碼疾棵,我們?yōu)閯?dòng)態(tài)轉(zhuǎn)發(fā)方法添加返回值和參數(shù),并在forwardInvocation方法中修改方法的返回值及參數(shù)痹仙。
// 3.消息轉(zhuǎn)發(fā)階段(未進(jìn)行動(dòng)態(tài)解析或者動(dòng)態(tài)解析失敗進(jìn)入本階段)
- (id)forwardingTargetForSelector:(SEL)aSelector {
if (aSelector == @selector(roar)) {
return [[Lion alloc] init];
}
return [super forwardingTargetForSelector:aSelector];
}
// 創(chuàng)建方法簽名
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
if (aSelector == @selector(hunt)) {
return [[[Lion alloc] init] methodSignatureForSelector:aSelector];
}
if (aSelector == @selector(offspringCountWithAge:)) {
return [NSMethodSignature signatureWithObjCTypes:"i@:i"];
}
return [super methodSignatureForSelector:aSelector];
}
/// 如果上一步‘methodSignatureForSelector:’返回正確的方法簽名是尔,則執(zhí)行此步。這里需要注意修改NSInvocation的target對(duì)象开仰,由于修改前的對(duì)象是原始調(diào)用對(duì)象未實(shí)現(xiàn)該方法拟枚,所以一直進(jìn)行消息轉(zhuǎn)發(fā),造成調(diào)用棧溢出
/// @param anInvocation anInvocation众弓,封裝了上一步分方法簽名
- (void)forwardInvocation:(NSInvocation *)anInvocation {
if (anInvocation.selector == @selector(hunt)) {
NSLog(@"NSInvocation original target is %@", anInvocation.target);
Lion *target = [[Lion alloc] init];
anInvocation.target = target;
[anInvocation invoke];
// [anInvocation invokeWithTarget:[[Lion alloc] init]];
NSLog(@"NSInvocation destination target is %@", anInvocation.target);
} else if (anInvocation.selector == @selector(offspringCountWithAge:)) {
int age;
// 獲取方法的參數(shù)恩溅,方法默認(rèn)還有self和cmd兩個(gè)參數(shù),因此新添加的參數(shù)下標(biāo)為2
[anInvocation getArgument:&age atIndex:2];
NSLog(@"offspringCountWithAge:修改前參數(shù)的值 = %d",age);
age = 3;
NSLog(@"offspringCountWithAge:修改后參數(shù)的值 = %d",age);
[anInvocation setArgument:&age atIndex:2];
[anInvocation invokeWithTarget:[[Lion alloc] init]];
// 獲取方法的返回值
int offspringCountWithAge;
[anInvocation getReturnValue:&offspringCountWithAge];
NSLog(@"offspringCountWithAge:返回值 = %d",offspringCountWithAge); // result = 220,說(shuō)明參數(shù)修改成功
offspringCountWithAge = 100;
[anInvocation setReturnValue:&offspringCountWithAge];
[anInvocation getReturnValue:&offspringCountWithAge];
NSLog(@"offspringCountWithAge:修改后返回值為 = %d",offspringCountWithAge);
}
}