iOS消息轉(zhuǎn)發(fā)機(jī)制Demo解析
假設(shè)說我們聲明一個(gè)類, 初始化對(duì)象, 并且在此類聲明一個(gè)方法, 調(diào)用方法的時(shí)候底層是怎么處理的呢? 今天我們來(lái)簡(jiǎn)單模擬測(cè)試, 來(lái)看道理發(fā)生了什么
以下是調(diào)用方法處理的方案圖, 按照方案順序去處理

以下是系統(tǒng)方法
- //消息轉(zhuǎn)發(fā)
- //- (id)forwardingTargetForSelector:(SEL)aSelector OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
- //標(biāo)準(zhǔn)的消息轉(zhuǎn)發(fā)
- //- (void)forwardInvocation:(NSInvocation *)anInvocation OBJC_SWIFT_UNAVAILABLE("");
- //- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector OBJC_SWIFT_UNAVAILABLE("");
- //
- //動(dòng)態(tài)方法解析
- //+ (BOOL)resolveClassMethod:(SEL)sel OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
- //+ (BOOL)resolveInstanceMethod:(SEL)sel OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
Demo解析
基礎(chǔ)步驟
創(chuàng)建類, Person類, 聲明方法, 并且在ViewController進(jìn)行初始化調(diào)用
- VC中
- Person *person = [Person new];
- [person run];
- Person類中
- - (void)run;//沒有實(shí)現(xiàn)
此時(shí)運(yùn)行是不是會(huì)報(bào)錯(cuò)呢? 就是這個(gè)常見的錯(cuò)誤
“ - [Person run]: unrecognized selector sent to instance 0x600000008310’ “
那么這樣做到底發(fā)生了什么? 做了哪些事情? 我們一步步來(lái)剖析
動(dòng)態(tài)測(cè)試
在Presenter類中, 寫動(dòng)態(tài)方法
- + (BOOL)resolveInstanceMethod:(SEL)sel{
- NSLog(@"sel = %@",NSStringFromSelector(sel));
- return [super resolveInstanceMethod:sel];
- }
再次運(yùn)行Demo就會(huì)走到這個(gè)方法中, 也就是我們所指的方案1, 此時(shí)打印出來(lái)的scl為” 消息轉(zhuǎn)發(fā)機(jī)制Demo[41829:4186268] sel = run “
解析模擬
- + (BOOL)resolveInstanceMethod:(SEL)sel{
- NSLog(@"sel = %@",NSStringFromSelector(sel));
- //1.判斷沒有實(shí)現(xiàn)方法, 那么我們就是動(dòng)態(tài)添加一個(gè)方法
- if (sel == @selector(run:)) {
- class_addMethod(self, sel, (IMP)newRun, "v@:@:");
- return YES;
- }
- return [super resolveInstanceMethod:sel];
- }
聲明函數(shù)
- void newRun(id self,SEL sel,NSString *str) {
- NSLog(@"---runok---%@",str);
- }
溫馨小提示, 動(dòng)態(tài)添加方法參數(shù)意譯 : //將要添加方法的類/sel名/IMP函數(shù)指針<添加函數(shù)>, 官方文檔其實(shí)是有解釋的
此時(shí)我們?cè)俅芜\(yùn)行, 那么打印結(jié)果就來(lái)了” 消息轉(zhuǎn)發(fā)機(jī)制Demo[43269:4212899] —runok—ok跑 “, 這樣的話我們就解決掉了報(bào)錯(cuò)這個(gè)問題
消息轉(zhuǎn)發(fā)重定向測(cè)試
此時(shí)我們新創(chuàng)建一個(gè)類Mbxb, 此時(shí)我們還是重新寫一個(gè)同名字的方法run方法, 并且進(jìn)行實(shí)現(xiàn)
- - (void)run{
- NSLog(@"---Mbxbrunok---");
- }
解析
此時(shí)有兩個(gè)同樣的方法, 我們重新在Person類中
來(lái)實(shí)現(xiàn)方法
- - (id)forwardingTargetForSelector:(SEL)aSelector{
- NSLog(@"aSelector = %@",NSStringFromSelector(aSelector));
- return [super forwardingTargetForSelector:aSelector];
- }
此時(shí)運(yùn)行測(cè)試, 動(dòng)態(tài)測(cè)試輸出” 消息轉(zhuǎn)發(fā)機(jī)制Demo[45875:4255869] sel = run “, 消息轉(zhuǎn)發(fā)重定向輸出” 消息轉(zhuǎn)發(fā)機(jī)制Demo[45875:4255869] —Mbxbrunok— “, 同樣也可以找見方法run
當(dāng)我們進(jìn)行處理
- - (id)forwardingTargetForSelector:(SEL)aSelector{
- NSLog(@"aSelector = %@",NSStringFromSelector(aSelector));
- return [[Mbxb alloc]init];
- }
那么此時(shí)運(yùn)行成功輸出, “ —Mbxbrunok— “
生成方法簽名轉(zhuǎn)發(fā)消息
此時(shí)我們?cè)赑erson類中, 生成方法簽名
- - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector OBJC_SWIFT_UNAVAILABLE(""){
- //轉(zhuǎn)化字符
- NSString *sel = NSStringFromSelector(aSelector);
- //判斷, 手動(dòng)生成簽名
- if([sel isEqualToString:@"run"]){
- return [NSMethodSignature signatureWithObjCTypes:"v@:"];
- }else{
- return [super methodSignatureForSelector:aSelector];
- }
拿到簽名
- - (void)forwardInvocation:(NSInvocation *)anInvocation OBJC_SWIFT_UNAVAILABLE(""){
- NSLog(@"---%@---",anInvocation);
- return [super forwardInvocation:anInvocation];
- }
此時(shí)我們的po的簽名輸出為” return value: {v} void target: {@} 0x600000016ba0 selector: {:} run “
拿到消息轉(zhuǎn)發(fā)簽名
- - (void)forwardInvocation:(NSInvocation *)anInvocation OBJC_SWIFT_UNAVAILABLE(""){
- NSLog(@"---%@---",anInvocation);
- //取到消息
- SEL seletor = [anInvocation selector];
- //轉(zhuǎn)發(fā)
- Mbxb *bxb = [[Mbxb alloc]init];
- if([bxb respondsToSelector:seletor]){
- //調(diào)用對(duì)象,進(jìn)行轉(zhuǎn)發(fā)
- [anInvocation invokeWithTarget:bxb];
- }else{
- return [super forwardInvocation:anInvocation];
- }
- }
小細(xì)節(jié): 拋出異常
假如說我們沒有這個(gè)方法, 同樣是遇到會(huì)崩潰的問題
我們這里來(lái)進(jìn)行一個(gè)異常處理
- - (void)doesNotRecognizeSelector:(SEL)aSelector{
- NSString *selStr = NSStringFromSelector(aSelector);
- NSLog(@"%@不存在",selStr);
- }
我們可以在這個(gè)異常處理中一些處理, 比如說彈框
總結(jié)
對(duì)于消息轉(zhuǎn)發(fā)機(jī)制, 我們重新來(lái)梳理一下Demo解析思路
還是三個(gè)方案, 按順序來(lái)走
- 動(dòng)態(tài)方法解析
- 消息轉(zhuǎn)發(fā)重定向
- 生成方法簽名
- 拿到簽名轉(zhuǎn)發(fā)消息
- 細(xì)節(jié)處理, 拋出異常
- 最后獻(xiàn)上一張邏輯圖

好了, 給大家這個(gè)簡(jiǎn)單demo, 當(dāng)然在代碼中也寫了注釋, 可以去我的git下載, 歡迎star