基本定義
IMP是一個(gè)函數(shù)指針,它在Runtime中的定義如下:
/// A pointer to the function of a method implementation.
typedef void (IMP)(void / id, SEL, ... */ );
IMP這個(gè)函數(shù)指針指向了方法實(shí)現(xiàn)的首地址,當(dāng)OC發(fā)起消息后,最終執(zhí)行的代碼是由IMP指針決定的。利用這個(gè)特性,我們可以對(duì)代碼進(jìn)行優(yōu)化:當(dāng)需要大量重復(fù)調(diào)用方法的時(shí)候,我們可以繞開消息綁定而直接利用IMP指針調(diào)起方法,這樣的執(zhí)行將會(huì)更加高效,相關(guān)的代碼示例如下:
void (*setter)(id, SEL, BOOL);
int i;
setter = (void (*)(id, SEL, BOOL))[target methodForSelector:@selector(setFilled:)];
for ( i = 0 ; i < 1000 ; i++ )
setter(targetList[i], @selector(setFilled:), YES);
注意:這里需要注意的就是函數(shù)指針的前兩個(gè)參數(shù)必須是id和SEL。
Runtime 消息發(fā)送
1.動(dòng)態(tài)方法解析(Dynamic Method Resolution)
所謂動(dòng)態(tài)解析,我們可以理解為通過cache和方法列表沒有找到方法時(shí),Runtime為我們提供一次動(dòng)態(tài)添加方法實(shí)現(xiàn)的機(jī)會(huì),主要用到的方法如下:
//OC方法:
//類方法未找到時(shí)調(diào)起,可于此添加類方法實(shí)現(xiàn)
+ (BOOL)resolveClassMethod:(SEL)sel
//實(shí)例方法未找到時(shí)調(diào)起,可于此添加實(shí)例方法實(shí)現(xiàn)
+ (BOOL)resolveInstanceMethod:(SEL)sel
//Runtime方法:
/**
運(yùn)行時(shí)方法:向指定類中添加特定方法實(shí)現(xiàn)的操作
@param cls 被添加方法的類
@param name selector方法名
@param imp 指向?qū)崿F(xiàn)方法的函數(shù)指針
@param types imp函數(shù)實(shí)現(xiàn)的返回值與參數(shù)類型
@return 添加方法是否成功
*/
BOOL class_addMethod(Class _Nullable cls,
SEL _Nonnull name,
IMP _Nonnull imp,
const char * _Nullable types)
2.消息接收者重定向
我們注意到動(dòng)態(tài)方法解析過程中的兩個(gè)resolve方法都返回了布爾值,當(dāng)它們返回YES時(shí)方法即可正常執(zhí)行,但是若它們返回NO,消息發(fā)送機(jī)制就進(jìn)入了消息轉(zhuǎn)發(fā)(Forwarding)的階段了,我們可以使用Runtime通過下面的方法替換消息接收者的為其他對(duì)象,從而保證程序的繼續(xù)執(zhí)行。
//重定向類方法的消息接收者,返回一個(gè)類
- (id)forwardingTargetForSelector:(SEL)aSelector
//重定向?qū)嵗椒ǖ南⒔邮苷?,返回一個(gè)實(shí)例對(duì)象
- (id)forwardingTargetForSelector:(SEL)aSelector
注意:動(dòng)態(tài)方法解析階段返回NO時(shí),我們可以通過forwardingTargetForSelector可以修改消息的接收者,該方法返回參數(shù)是一個(gè)對(duì)象,如果這個(gè)對(duì)象是非nil,非self,系統(tǒng)會(huì)將運(yùn)行的消息轉(zhuǎn)發(fā)給這個(gè)對(duì)象執(zhí)行。否則,繼續(xù)查找其他流程。
3.消息重定向
當(dāng)以上兩種方法無法生效,那么這個(gè)對(duì)象會(huì)因?yàn)檎也坏较鄳?yīng)的方法實(shí)現(xiàn)而無法響應(yīng)消息,此時(shí)Runtime系統(tǒng)會(huì)通過forwardInvocation:消息通知該對(duì)象,給予此次消息發(fā)送最后一次尋找IMP的機(jī)會(huì):
- (void)forwardInvocation:(NSInvocation *)anInvocation;
其實(shí)每個(gè)對(duì)象都從NSObject類中繼承了forwardInvocation:方法,但是NSObject中的這個(gè)方法只是簡單的調(diào)用了doesNotRecongnizeSelector:方法,提示我們錯(cuò)誤。所以我們可以重寫這個(gè)方法:對(duì)不能處理的消息做一些默認(rèn)處理,也可以將消息轉(zhuǎn)發(fā)給其他對(duì)象來處理,而不拋出錯(cuò)誤。
我們注意到anInvocation是forwardInvocation唯一參數(shù),它封裝了原始的消息和消息參數(shù)。正是因?yàn)樗?,我們還不得不重寫另一個(gè)函數(shù):methodSignatureForSelector。這是因?yàn)樵趂orwardInvocation: 消息發(fā)送前,Runtime系統(tǒng)會(huì)向?qū)ο蟀l(fā)送methodSignatureForSelector消息,并取到返回的方法簽名用于生成NSInvocation對(duì)象。
http://www.itdecent.cn/p/d4b55dae9a0d
http://www.itdecent.cn/p/fe131f8757ba