大家都知道oc是動(dòng)態(tài)語(yǔ)言杨箭,是runtime的膝蜈,怎么體現(xiàn)他的動(dòng)態(tài)性呢鹅心,今天用SEL來(lái)分析下吕粗,之前分享過(guò)鉤子其實(shí)就可以看出來(lái)了,現(xiàn)在具體看看OC是怎么通過(guò)SEL找到函數(shù)的巴帮。
[self performSelector:@selector(abcd)];
[self abcd];? //這種方法可能都會(huì)編譯不過(guò)去
假如類方法里面我們便沒(méi)有寫(xiě)adcd方法溯泣,這樣就會(huì)奔潰了虐秋,oc是怎么做的呢
該方法調(diào)用后榕茧,OC會(huì)發(fā)出objc_msgSend,將消息傳給本來(lái)查找本類中的SEL是否存在Method
假設(shè)本類不存在會(huì)查找工程里是否有分類提供了該方法
假設(shè)分類中也沒(méi)有該方法客给,系統(tǒng)會(huì)將消息轉(zhuǎn)發(fā)給其父類用押,父類收到消息后也會(huì)執(zhí)行上面的操作,找方法靶剑,沒(méi)找到再往上轉(zhuǎn)發(fā)消息
假設(shè)最終都沒(méi)有找到蜻拨,就會(huì)執(zhí)行最后的消息轉(zhuǎn)發(fā)(message forwarding)操作
如果轉(zhuǎn)發(fā)出去都沒(méi)人接收的話,NSObject中的doesNotRecognizeSelector就選擇拋出異常了桩引,也就是我們看到的crash
上面的過(guò)程有點(diǎn)復(fù)雜缎讼,大家會(huì)覺(jué)得這樣很慢,第一次的確很慢坑匠,所性objc_msgSend會(huì)將匹配結(jié)果寫(xiě)到映射表中緩存起來(lái)血崭,每個(gè)類都有這樣的一塊緩存
整個(gè)過(guò)程就是上面將的那樣,我們講一下第4項(xiàng)消息轉(zhuǎn)發(fā)怎么處理吧:
消息轉(zhuǎn)發(fā)還分兩個(gè)階段:
1.動(dòng)態(tài)方法解析:意思就是說(shuō)詢問(wèn)接受者要不要增加個(gè)方法來(lái)實(shí)現(xiàn)該函數(shù)
+ (BOOL)resolveInstanceMethod:(SEL)selecotor? //對(duì)象方法
+ (BOOL)resolveClassMethod:(SEL)sel? ? ? ? ? ? //類方法
在方法里可以動(dòng)態(tài)給類增加一個(gè)方法Method
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self performSelector:@selector(abcd)];
}
+ (BOOL)resolveInstanceMethod:(SEL)sel {
NSString *selname = NSStringFromSelector(sel);
if ([selname isEqualToString:@"abcd"]) {
class_addMethod(self, sel, class_getMethodImplementation([self class], @selector(oklala)), method_getTypeEncoding(class_getInstanceMethod([self class], @selector(oklala))));
return YES;
}
return [super resolveInstanceMethod:sel];
}
- (void)oklala {
NSLog(@"oklala");
}
2.完整的消息轉(zhuǎn)發(fā):看看還有沒(méi)有別的對(duì)象要處理,有轉(zhuǎn)出夹纫,沒(méi)有的話封裝成NSInvocation對(duì)象處理
1)假如可以轉(zhuǎn)給別的對(duì)象處理:
- (id)forwardingTargetForSelector:(SEL)aSelector
可以將要轉(zhuǎn)發(fā)的對(duì)象返回咽瓷。
@interface abcdCaller : NSObject
@end
@implementation abcdCaller
- (void)abcd {
NSLog(@"~~~~~~");
}
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self performSelector:@selector(abcd)];
}
- (id)forwardingTargetForSelector:(SEL)aSelector {
NSLog(@"!!!!!!!!!!!!!");
return [[abcdCaller alloc] init];
}
@end
2)假如沒(méi)有對(duì)象可以轉(zhuǎn)發(fā)(完整消息轉(zhuǎn)發(fā))
- (void)forwardInvocation:(NSInvocation *)anInvocation
最后可以處理消息的機(jī)會(huì)
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self performSelector:@selector(abcd)];
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
NSMethodSignature *sig = [NSMethodSignature signatureWithObjCTypes:"@@:"];
return sig;
}
- (void)forwardInvocation:(NSInvocation *)anInvocation {
NSLog(@"~~~~%@", [anInvocation description]);
}
---------------------------------------------------凌亂的分割線-------------------------------------------------
假設(shè)你的消息轉(zhuǎn)發(fā)不處理,那么在第5步捕獲異常舰讹,也是不會(huì)奔潰的
@try {
[self performSelector:@selector(abcd)];
}
@catch (NSException *exception) {
NSLog(@"~~~~~~~~");
}
@finally {
}