簡(jiǎn)介:
[object doSomething];
編譯器會(huì)轉(zhuǎn)換為下面形式:
objc_msgSend(receiver, selector);
如果消息含有參數(shù),則為:
objc_msgSend(receiver, selector, arg1, arg2, ...)
1、消息發(fā)送機(jī)制
- 檢查selector 是否需要忽略(比如 Mac OS X 開(kāi)發(fā)萍桌,有了垃圾回收就不理會(huì) retain,release 這些函數(shù)凌简。)
- 檢查target是否為nil上炎,如果為nil,直接cleanup雏搂,然后return(這就是為什么可以向nil發(fā)送消息的原因)
- 然后在target的Class中根據(jù)Selector去找IMP反症。
尋找IMP的過(guò)程:
- 先從當(dāng)前class的cache方法列表(cache methodLists)里去找。
- 如果找到了畔派,跳到對(duì)應(yīng)函數(shù)實(shí)現(xiàn)。
- 如果沒(méi)找到润绵,就從class的方法列表(methodLists)里找线椰。
- 如果還找不到,就到super class的方法列表里找尘盼,直到找到基類(lèi)(NSObject)為止憨愉。
- 最后再找不到,就會(huì)進(jìn)入動(dòng)態(tài)方法解析和消息轉(zhuǎn)發(fā)的機(jī)制卿捎。
2配紫、消息轉(zhuǎn)發(fā)機(jī)制
- 消息發(fā)送是Runtime通過(guò)selector快速查找IMP的過(guò)程,有了函數(shù)指針就可以執(zhí)行對(duì)應(yīng)的方法實(shí)現(xiàn)午阵;
- 消息轉(zhuǎn)發(fā)是在查找IMP失敗后執(zhí)行一系列轉(zhuǎn)發(fā)流程的慢速通道躺孝,如果不作轉(zhuǎn)發(fā)處理享扔,則會(huì)打日志和拋出異常。
①動(dòng)態(tài)方法解析:
對(duì)象在收到無(wú)法解讀的消息之后植袍,首先將調(diào)用所屬類(lèi)的下列類(lèi)方法:
+(BOOL)resolveInstanceMethod:(SEL)name
demo解析:
#import <Foundation/Foundation.h>@interface Person : NSObject
- (void)eat; //沒(méi)有實(shí)現(xiàn)
@end
運(yùn)行結(jié)果在person.m中寫(xiě)入以下代碼惧眠,對(duì)象在接受的無(wú)法解讀的消息后,首先會(huì)調(diào)用+(BOOL)resolveInstanceMethod:(SEL)sel或者+ (BOOL)resolveClassMethod:(SEL)sel, 詢(xún)問(wèn)是否有動(dòng)態(tài)添加方法來(lái)進(jìn)行處理于个,處理實(shí)例如下
@implementation Person
+(BOOL)resolveInstanceMethod:(SEL)sel
{
NSLog(@"sel = %@",NSStringFromSelector(sel));
return [super resolveInstanceMethod:sel];
}
結(jié)果如下:具體添加方法如下:
+ (BOOL)resolveInstanceMethod:(SEL)sel {
if (sel == @selector(eat))
{
class_addMethod([self class], sel, (IMP)addeat, "V@");
return YES;
}
return [super resolveInstanceMethod:sel];
}
void addeat(id self,SEL sel,NSString *str)
{
NSLog(@"添加成功");
}
class_addMethod(Class _Nullable cls, SEL _Nonnull name, IMP _Nonnull imp, const char * _Nullable types)
參數(shù)一:被添加方法的累
參數(shù)二:要添加的方法
參數(shù)三:實(shí)現(xiàn)這個(gè)方法的函數(shù)
參數(shù)四:定義返回值和參數(shù)類(lèi)型的字符串(i:代表int v代表void @:代表參數(shù))
如果動(dòng)態(tài)添加失敗氛魁,就會(huì)進(jìn)入下面的操作
②消息轉(zhuǎn)發(fā)重定向
- (id)forwardingTargetForSelector:(SEL)aSelector
在消息轉(zhuǎn)發(fā)機(jī)制執(zhí)行前,Runtime系統(tǒng)會(huì)再給我們一次偷梁換柱的機(jī)會(huì)厅篓,即通過(guò)重載- (id)forwardingTargetForSelector:(SEL)aSelector方法替換消息的接受者為其他對(duì)象:
新建一個(gè)tempObject秀存,定義eat方法,在.m文件中實(shí)現(xiàn)
#import "tempObject.h"
@implementation tempObject
- (void)eat
{
NSLog(@"i am tempObject");
}
@end
在person類(lèi)中將接受消息對(duì)象換為tempObject
-(id)forwardingTargetForSelector:(SEL)aSelector
{
return [[tempObject alloc]init];
}
結(jié)果如下:
③ 消息轉(zhuǎn)發(fā)
首先獲取方法的簽名羽氮,拿著簽名再去配發(fā)消息或链,如果不能接受消息就會(huì)拋出異常
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector;
- (void)forwardInvocation:(NSInvocation *)anInvocation;
獲取方法簽名
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector OBJC_SWIFT_UNAVAILABLE("")
{
//轉(zhuǎn)化字符
NSString *sel = NSStringFromSelector(aSelector);
//判斷, 手動(dòng)生成簽名
if([sel isEqualToString:@"eat"])
{
return [NSMethodSignature signatureWithObjCTypes:"v@:"];
}
else
{
return [super methodSignatureForSelector:aSelector];
}
}
拿到方法簽名配發(fā)消息
- (void)forwardInvocation:(NSInvocation *)anInvocation OBJC_SWIFT_UNAVAILABLE(""){
NSLog(@"forwardInvocation: %@", NSStringFromSelector([anInvocation selector]));
//取到消息
SEL seletor = [anInvocation selector];
//轉(zhuǎn)發(fā)
tempObject *temp = [[tempObject alloc]init];
if([temp respondsToSelector:seletor])
{
//調(diào)用對(duì)象,進(jìn)行轉(zhuǎn)發(fā)
[anInvocation invokeWithTarget:temp];
}
else
{
return [super forwardInvocation:anInvocation];
}
}
如果不能接受消息,拋出異常
- (void)doesNotRecognizeSelector:(SEL)aSelector
{
NSString *selStr = NSStringFromSelector(aSelector);
NSLog(@"%@不存在",selStr);
}
參考文章:
runtime 消息轉(zhuǎn)發(fā):http://www.reibang.com/p/8cd06cd496d5
? runtime消息轉(zhuǎn)發(fā)demo:http://www.2bjs.com/%E6%9E%B6%E6%9E%84/iOS%20%E6%B6%88%E6%81%AF%E8%BD%AC%E5%8F%91%E6%9C%BA%E5%88%B6Demo%E8%A7%A3%E6%9E%90/