消息轉(zhuǎn)發(fā)流程圖
15277558865032.jpg
如果類接收到無法處理的消息,會(huì)觸發(fā)消息轉(zhuǎn)發(fā)機(jī)制,一共有三個(gè)步驟涝登,接受者在每一步中均有機(jī)會(huì)處理消息。步驟越往后朽基,處理消息的代價(jià)就越大,所以最好再第一步就處理完离陶。
第一道防線
在類里面實(shí)現(xiàn)兩個(gè)方法來處理未知消息稼虎。執(zhí)行動(dòng)態(tài)方法解析之前,先會(huì)判斷是否曾經(jīng)有動(dòng)態(tài)解析枕磁。
-
resolveInstanceMethod
:處理實(shí)例方法 -
resolveClassMethod
:處理類方法
我們來看個(gè)Demo渡蜻,先看調(diào)用方代碼
TestA *testA = [[TestA alloc] init];
[testA instanceMethod];
[TestA classMethod];
再來看看TestA的定義。
// TestA.h
@interface TestA : NSObject
- (void)instanceMethod;
+ (void)classMethod;
@end
// TestA.m
@implementation TestA
- (void)newInstanceMethod {
NSLog(@"newInstanceMethod");
}
+ (void)newClassMethod {
NSLog(@"newClassMethod");
}
+ (BOOL)resolveInstanceMethod:(SEL)sel {
if (sel == @selector(instanceMethod)) {
// 動(dòng)態(tài)添加方法newInstanceMethod
Method method = class_getInstanceMethod([self class], @selector(newInstanceMethod));
IMP imp = method_getImplementation(method);
class_addMethod([self class], sel, imp, method_getTypeEncoding(method));
// 成功處理计济,消息轉(zhuǎn)發(fā)機(jī)制結(jié)束茸苇,調(diào)用newInstanceMethod
return YES;
}
// 不能處理,進(jìn)入第二步
return [super resolveInstanceMethod:sel];
}
+ (BOOL)resolveClassMethod:(SEL)sel {
if (sel == @selector(classMethod)) {
// 動(dòng)態(tài)添加方法newClassMethod
Method method = class_getInstanceMethod(object_getClass(self), @selector(newClassMethod));
IMP imp = method_getImplementation(method);
class_addMethod(object_getClass(self), sel, imp, method_getTypeEncoding(method));
// 成功處理沦寂,消息轉(zhuǎn)發(fā)機(jī)制結(jié)束学密,調(diào)用newClassMethod
return YES;
}
// 不能處理,進(jìn)入第二步
return [super resolveClassMethod:sel];
}
@end
TestA中頭文件定義了兩個(gè)方法传藏,但是沒有實(shí)現(xiàn)腻暮,如果不用消息轉(zhuǎn)發(fā)機(jī)制處理異常,會(huì)導(dǎo)致crash毯侦,log想必大家應(yīng)該很熟悉
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[TestA funcA]: unrecognized selector sent to instance 0x6040000125c0'
實(shí)例方法存儲(chǔ)在類對(duì)象哭靖,類方法存儲(chǔ)在元類對(duì)象,在調(diào)用class_addMethod
時(shí)侈离,第一個(gè)參數(shù)需要注意试幽。
第二道防線
第二道防線依賴一個(gè)函數(shù)forwardingTargetForSelector
。
// 類方法
//+ (id)forwardingTargetForSelector:(SEL)aSelector {
//
//}
- (id)forwardingTargetForSelector:(SEL)aSelector {
if (aSelector == @selector(instanceMethod)) {
// 消息轉(zhuǎn)發(fā)給TestB實(shí)例
return [TestB new];
}
// 消息轉(zhuǎn)發(fā)失敗卦碾,進(jìn)入下一步
return nil;
}
// TestB.m
- (void)instanceMethod {
NSLog(@"instanceMethod");
}
第三道防線
第三道防線有兩步
- 調(diào)用
methodSignatureForSelector
铺坞,獲取新的方法簽名(返回值類型起宽,參數(shù)類型) - 調(diào)用
forwardInvocation
,轉(zhuǎn)發(fā)消息济榨,
// 方法簽名(返回值類型坯沪,參數(shù)類型)
// 類方法減號(hào)改為加號(hào)
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
NSMethodSignature *signature = [TestB instanceMethodSignatureForSelector:aSelector];
return signature;
}
// NSInvocation封裝了方法調(diào)用,包括:方法調(diào)用者擒滑、方法名腐晾、方法參數(shù)
// anInvocation.target 消息接受者
// anInvocation.selector 函數(shù)名
// [anInvocation getArgument:NULL atIndex:0]; 獲取參數(shù)
// 類方法減號(hào)改為加號(hào)
- (void)forwardInvocation:(NSInvocation *)anInvocation {
[anInvocation invokeWithTarget:[TestB new]];
}
歡迎關(guān)注我的博客