前言:我是參考 南峰子 的博客加上自己理解寫的芳肌,原著專輯大家自己可看:http://southpeak.github.io/categories/objectivec/
一,簡介:Method
1. 在OC 中方法我們用Method
來表示味咳,它的定義是一個結(jié)構(gòu)體庇勃,如下:
typedef struct objc_method *Method;
struct objc_method {
SEL method_name OBJC2_UNAVAILABLE; // 方法名
char *method_types OBJC2_UNAVAILABLE;
IMP method_imp OBJC2_UNAVAILABLE; // 方法實現(xiàn)
}
可以看出其主要是:SEL
、IMP
槽驶、char *method_types
三部分組成责嚷,我們可以分別來看看:
2. SEL
SEL又叫選擇器,是表示一個方法的selector的指針掂铐,其定義如下:
typedef struct objc_selector *SEL;
方法的selector
用于表示運行時方法的名字罕拂。Objective-C在編譯時,會依據(jù)每一個方法的名字全陨、參數(shù)序列爆班,生成一個唯一的整型標識(Int
類型的地址),這個標識就是SEL
辱姨。如下代碼所示:
SEL sel1 = @selector(method1);
NSLog(@"sel : %p", sel1);
上面的輸出為:
2014-10-30 18:40:07.518 RuntimeTest[52734:466626] sel : 0x100002d72
兩個類之間柿菩,不管它們是父類與子類的關(guān)系,還是之間沒有這種關(guān)系雨涛,只要方法名相同枢舶,那么方法的SEL就是一樣的懦胞。每一個方法都對應(yīng)著一個SEL。所以在Objective-C同一個類(及類的繼承體系)中凉泄,不能存在2個同名的方法躏尉,即使參數(shù)類型不同也不行。相同的方法只能對應(yīng)一個SEL后众。這也就導(dǎo)致Objective-C在處理相同方法名且參數(shù)個數(shù)相同但類型不同的方法方面的能力很差胀糜。
當然,不同的類可以擁有相同的selector蒂誉,這個沒有問題教藻。不同類的實例對象執(zhí)行相同的selector時,會在各自的方法列表中去根據(jù)selector去尋找自己對應(yīng)的IMP
工程中的所有的SEL組成一個Set集合拗盒,Set的特點就是唯一怖竭,因此SEL是唯一的。因此陡蝇,如果我們想到這個方法集合中查找某個方法時,只需要去找到這個方法對應(yīng)的SEL就行了哮肚,SEL實際上就是根據(jù)方法名hash化了的一個字符串登夫,而對于字符串的比較僅僅需要比較他們的地址就可以了,可以說速度上無語倫比T侍恕恼策!但是,有一個問題潮剪,就是數(shù)量增多會增大hash沖突而導(dǎo)致的性能下降(或是沒有沖突涣楷,因為也可能用的是perfect hash)。但是不管使用什么樣的方法加速抗碰,如果能夠?qū)⒖偭繙p少(多個方法可能對應(yīng)同一個SEL)狮斗,那將是最犀利的方法。那么弧蝇,我們就不難理解碳褒,為什么SEL僅僅是函數(shù)名了。
其實說了這么多我總結(jié)的就一個SEL
就是根據(jù)方法名稱得到字符串看疗,其已經(jīng)忽略了所帶的參數(shù)是什么沙峻,就是在OC中代表某個方法,它最后要和IMP(具體執(zhí)行的C函數(shù)两芳,標準的C調(diào)用)進行映射綁定摔寨,下面我們就來介紹IMP
3. IMP
實際上是一個函數(shù)指針,指向方法實現(xiàn)的首地址怖辆。其定義如下:
id (*IMP)(id, SEL, ...)
這個函數(shù)使用當前CPU架構(gòu)實現(xiàn)的標準的C調(diào)用約定是复。第一個參數(shù)是指向self的指針(如果是實例方法沉填,則是類實例的內(nèi)存地址;如果是類方法佑笋,則是指向元類的指針)翼闹,第二個參數(shù)是方法選擇器(selector),接下來是方法的實際參數(shù)列表蒋纬。
前面介紹過的SEL就是為了查找方法的最終實現(xiàn)IMP的猎荠。由于每個方法對應(yīng)唯一的SEL,因此我們可以通過SEL
方便快速準確地獲得它所對應(yīng)的IMP蜀备,查找過程將在下面討論关摇。取得IMP后,我們就獲得了執(zhí)行這個方法代碼的入口點碾阁,此時输虱,我們就可以像調(diào)用普通的C語言函數(shù)一樣來使用這個函數(shù)指針了。
總結(jié)的說IMP其實就是C函數(shù)指針脂凶,這個函數(shù)必須要傳入兩個參數(shù)第一個是執(zhí)行該函數(shù)的對象宪睹,第二個就是關(guān)聯(lián)的SEL,剩下跟著的參數(shù)就是方法出入的參數(shù)了蚕钦,你后面跟多少亭病,看你自己的了!
二嘶居,方法的調(diào)用流程
1罪帖,基本調(diào)用
在Objective-C中,消息直到運行時才綁定到方法實現(xiàn)上邮屁。編譯器會將消息表達式[receiver message]轉(zhuǎn)化為一個消息函數(shù)的調(diào)用整袁,即objc_msgSend。這個函數(shù)將消息接收者和方法名作為其基礎(chǔ)參數(shù)佑吝,如以下所示
objc_msgSend(receiver, selector)
如果有參數(shù):
objc_msgSend(receiver, selector, arg1, arg2, ...)
1坐昙,首先它找到selector對應(yīng)的方法實現(xiàn)。因為同一個方法可能在不同的類中有不同的實現(xiàn)迹蛤,所以我們需要依賴于接收者的類來找到的確切的實現(xiàn)民珍。
2,它調(diào)用方法實現(xiàn)盗飒,并將接收者對象及方法的所有參數(shù)傳給它嚷量。
3,最后逆趣,它將實現(xiàn)返回的值作為它自己的返回值蝶溶。
通俗而言,我們在用一個實例或者是類調(diào)用一個方法,這個實例或者是類就是Receiver抖所,方法的字符串就是對應(yīng)selector梨州,這個方法對應(yīng)的參數(shù)就往后面依次排開agr1,arg2.....
2, 消息轉(zhuǎn)發(fā)(message forwarding)
這才是真正核心的地方,其實上面講的基本調(diào)用也就是正常轉(zhuǎn)發(fā)田轧,但如果一個對象無法接收指定消息時暴匠,又會發(fā)生什么事呢?默認情況下傻粘,如果是以[object message]的方式調(diào)用方法每窖,如果object無法響應(yīng)message消息時,編譯器會報錯弦悉。但如果是以perform...的形式來調(diào)用窒典,則需要等到運行時才能確定object是否能接收message消息。如果不能稽莉,則程序崩潰瀑志。通常,當我們不能確定一個對象是否能接收某個消息時污秆,會先調(diào)用respondsToSelector:
來判斷一下劈猪。當一個對象無法接收某一消息時,就會啟動所謂”消息轉(zhuǎn)發(fā)(message forwarding)“機制混狠,通過這一機制岸霹,我們可以告訴對象如何處理未知的消息。默認情況下将饺,對象接收到未知的消息,會導(dǎo)致程序崩潰,如下代碼所示:
unrecognized selector sent to instance 0x100111940
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[SUTRuntimeMethod method]: unrecognized selector sent to instance 0x100111940'
這段異常信息實際上是由NSObject的”doesNotRecognizeSelector“方法拋出的痛黎。不過予弧,我們可以采取一些措施,讓我們的程序執(zhí)行特定的邏輯湖饱,而避免程序的崩潰掖蛤。
消息轉(zhuǎn)發(fā)機制基本上分為三個步驟:
1>,動態(tài)方法解析------->2>,備用接收者-------->3>,完整消息轉(zhuǎn)發(fā)
1>動態(tài)方法解析
對象在接收到未知的消息時,首先會調(diào)用所屬類的類方法+resolveInstanceMethod:(實例方法)或者+resolveClassMethod:(類方法)井厌。在這個方法中蚓庭,我們有機會為該未知消息新增一個”處理方法””。不過使用該方法的前提是我們已經(jīng)實現(xiàn)了該”處理方法”仅仆,只需要在運行時通過class_addMethod函數(shù)動態(tài)添加到類里面就可以了器赞。
例如我又一個Person類,我在外面通過performSelector
調(diào)用了它的一個實例方法testMethod1
墓拜,并傳入了兩個參數(shù)
/*******方法傳遞***********/
Person *testPerson = [[Person alloc]init];
[testPerson performSelector:@selector(testMethod1) withObject:@"小胡" withObject:@"酒鬼"];
在我的Person類中我沒有testMethod1
這個方法港柜,我需要怎么處理了?
//C函數(shù)中IMP其實就是標準的 C函數(shù)調(diào)用
void IMPTestMethod1Funcation(id self,SEL _cmd,NSString *name,NSString *nick){
NSLog(@"IMP C 函數(shù)中實現(xiàn):%@, %p", self, _cmd);
NSLog(@"我?guī)氲膮?shù)是name:%@ nick:%@ ",name,nick);
}
+(BOOL)resolveInstanceMethod:(SEL)sel{
NSString *selString = NSStringFromSelector(sel);
NSLog(@"調(diào)用的方法是什么:%@",selString);
if ([selString isEqualToString:@"testMethod1"]) {
class_addMethod(self.class, sel, (IMP)IMPTestMethod1Funcation, "@:@@");
}
return [super resolveInstanceMethod:sel];
}
首先,當調(diào)用方法testMethod1
在Person找不對應(yīng)的方法夏醉,會進入+(BOOL)resolveInstanceMethod:(SEL)sel
這個方法中爽锥,在這里你可以通過判斷方法的名字(字符串)來判斷要處理的方法。然后通過class_addMethod
來為這個方法動態(tài)綁定一個實現(xiàn)方法畔柔。這個實現(xiàn)方法是一個標準的C調(diào)用方法氯夷,如例子中的:void IMPTestMethod1Funcation(id self,SEL _cmd,NSString *name,NSString *nick)
其中前兩個參數(shù)是必須的:
第一個id self
表示方法執(zhí)行的對象,
第二個SEL _cmd
表示這個C方法要替代那個OC方法方法的SEL靶擦。
后面就是被替代OC方法方法要根的參數(shù)(我這個OC方法有兩個參數(shù)腮考,所以后面跟上兩個參數(shù))
打印結(jié)果:
2016-10-31 15:30:06.205 ObRunTime[35215:6061873] 調(diào)用的方法是什么:testMethod1
2016-10-31 15:30:06.205 ObRunTime[35215:6061873] 調(diào)用的方法是什么:_dynamicContextEvaluation:patternString:
2016-10-31 15:30:06.206 ObRunTime[35215:6061873] 調(diào)用的方法是什么:descriptionWithLocale:
2016-10-31 15:30:06.206 ObRunTime[35215:6061873] IMP C 函數(shù)中實現(xiàn):<Person: 0x600000024440>, testMethod1
2016-10-31 15:30:06.206 ObRunTime[35215:6061873] 我?guī)氲膮?shù)是name:小胡 nick:酒鬼
關(guān)于
class_addMethod(Class cls, SEL name, IMP imp, const char *types)
第四個參數(shù)*types,解釋一下奢啥,比較讓人暈秸仙,這里摸索后解釋一下:
我這里用的是@:@@
,其實完整寫法是v@:@@
桩盲,其中v@:
表示返回值是void(沒有返回值)寂纪,后面的兩個@
分別表示這個方法傳入了兩個參數(shù)。如果你只傳了一個參數(shù)就用v@:@
就可以赌结。還有i@:
表示能有一個int返回值捞蛋,后面參數(shù)跟幾個同理了。
當然如果你覺的class_addMethod中添加的是IMP是一個C函數(shù)看的不習慣的話柬姚,可以這樣寫:
class_addMethod(self.class, sel, class_getMethodImplementation(self, @selector(你的oc方法)), "v@:@@");
//替換C函數(shù)的OC函數(shù)
-(void)test:(NSString *)agr1 agr2:(NSString *)agr2{
NSLog(@"我?guī)氲膮?shù)是name:%@ nick:%@ ",agr1,agr2);
NSLog(@"要做什么事TODO");
}
+(BOOL)resolveInstanceMethod:(SEL)sel{
NSString *selString = NSStringFromSelector(sel);
NSLog(@"調(diào)用的方法是什么:%@",selString);
if ([selString isEqualToString:@"testMethod1"]) {
// class_addMethod(self.class, sel, (IMP)IMPTestMethod1Funcation, "@:@@");
class_addMethod(self.class, sel, class_getMethodImplementation(self, @selector(test:agr2:)), "v@:@@");
}
return [super resolveInstanceMethod:sel];
}
打印結(jié)果:
2016-10-31 16:03:46.543 ObRunTime[35350:6168967] 調(diào)用的方法是什么:testMethod1
2016-10-31 16:03:46.543 ObRunTime[35350:6168967] 我?guī)氲膮?shù)是name:小胡 nick:酒鬼
2016-10-31 16:03:46.544 ObRunTime[35350:6168967] 要做什么事TODO
2>,備用接收者
如果在上一步無法處理消息拟杉,則Runtime會繼續(xù)調(diào)以下方法:
-(id)forwardingTargetForSelector:(SEL)aSelector
如果一個對象實現(xiàn)了這個方法,并返回一個非nil的結(jié)果量承,則這個對象會作為消息的新接收者搬设,且消息會被分發(fā)到這個對象。當然這個對象不能是self自身撕捍,否則就是出現(xiàn)無限循環(huán)拿穴。當然,如果我們沒有指定相應(yīng)的對象來處理aSelector忧风,則應(yīng)該調(diào)用父類的實現(xiàn)來返回結(jié)果默色。
使用這個方法通常是在對象內(nèi)部,可能還有一系列其它對象能處理該消息狮腿,我們便可借這些對象來處理消息并返回腿宰,這樣在對象外部看來,還是由該對象親自處理了這一消息缘厢。
如下我申明了一個ObjectHelper
類
#import "ObjectHelper.h"
@implementation ObjectHelper
-(void)testMethodHelp:(NSString *)string{
NSLog(@"方法轉(zhuǎn)移到這個類來實現(xiàn)了 參數(shù):%@",string);
}
@end
現(xiàn)在我?直接調(diào)用Person的這個方法吃度,因為Person沒有這個方法,我把它轉(zhuǎn)發(fā)給ObjectHelper
Person *testPerson = [[Person alloc]init];
[testPerson performSelector:@selector(testMethodHelp:) withObject:@"轉(zhuǎn)發(fā)測試"];
/***********消息轉(zhuǎn)發(fā)給別的類處理************/
//如果resolveInstanceMethod 無法處理消息 就會給你一次機會 讓你把這個方法分發(fā)的別的對象去處理
-(id)forwardingTargetForSelector:(SEL)aSelector{
NSString *selString = NSStringFromSelector(aSelector);
if ([selString isEqualToString:@"testMethodHelp:"]) {
return [[ObjectHelper alloc]init];
}
return [super forwardingTargetForSelector:aSelector];
}
控制臺打用列濉:
2016-10-31 17:43:20.447 ObRunTime[35692:6278785] 調(diào)用的方法是什么:testMethodHelp:
2016-10-31 17:43:20.447 ObRunTime[35692:6278785] 不是當前要研究的方法
2016-10-31 17:43:20.447 ObRunTime[35692:6278785] 方法轉(zhuǎn)移到這個類來實現(xiàn)了 參數(shù):轉(zhuǎn)發(fā)測試
3>,完整消息轉(zhuǎn)發(fā)
如果在上一步還不能處理未知消息规肴,則唯一能做的就是啟用完整的消息轉(zhuǎn)發(fā)機制了。此時會調(diào)用以下方法:
- (void)forwardInvocation:(NSInvocation *)anInvocation
運行時系統(tǒng)會在這一步給消息接收者最后一次機會將消息轉(zhuǎn)發(fā)給其它對象(不止一個對象,可以是多個)拖刃。對象會創(chuàng)建一個表示消息的NSInvocation對象删壮,把與尚未處理的消息有關(guān)的全部細節(jié)都封裝在anInvocation中,包括selector兑牡,目標(target)和參數(shù)央碟。我們可以在forwardInvocation方法中選擇將消息轉(zhuǎn)發(fā)給其它對象。
forwardInvocation:方法的實現(xiàn)有兩個任務(wù):
- 定位可以響應(yīng)封裝在anInvocation中的消息的對象均函。這個對象不需要能處理所有未知消息亿虽。
- 使用anInvocation作為參數(shù),將消息發(fā)送到選中的對象苞也。anInvocation將會保留調(diào)用結(jié)果洛勉,運行時系統(tǒng)會提取這一結(jié)果并將其發(fā)送到消息的原始發(fā)送者。
不過如迟,在這個方法中我們可以實現(xiàn)一些更復(fù)雜的功能收毫,我們可以對消息的內(nèi)容進行修改,比如追回一個參數(shù)等殷勘,然后再去觸發(fā)消息此再。另外,若發(fā)現(xiàn)某個消息不應(yīng)由本類處理玲销,則應(yīng)調(diào)用父類的同名方法输拇,以便繼承體系中的每個類都有機會處理此調(diào)用請求。
還有一個很重要的問題贤斜,我們必須重寫以下方法:
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
消息轉(zhuǎn)發(fā)機制使用從這個方法中獲取的信息來創(chuàng)建NSInvocation對象策吠。因此我們必須重寫這個方法,為給定的selector提供一個合適的方法簽名瘩绒。
其實簽名奴曙,可以理解找到一個實現(xiàn)該方法的類,然后把這個方法的名稱草讶、參數(shù)、返回值包裝起來炉菲,打一個包堕战!
下面舉一個例子,還是我的Person
類拍霜,我在ViewController
調(diào)用了一個他沒有實現(xiàn)的方法testMethodInvork
/*******消息完全轉(zhuǎn)發(fā)***********/
Person *testPerson = [[Person alloc]init];
[testPerson performSelector:@selector(testMethodInvork)];
我們把消息分發(fā)給ObjectHelper
處理嘱丢,ObjectHelper
實現(xiàn)testMethodInvork
方法
-(void)testMethodInvork{
NSLog(@"天啊,消息轉(zhuǎn)發(fā)了");
}
Person
中的實現(xiàn):
/**********處理未知消息的步驟**********/
//C函數(shù)中IMP其實就是標準的 C函數(shù)調(diào)用
void IMPTestMethod1Funcation(id self,SEL _cmd,NSString *name,NSString *nick){
NSLog(@"IMP C 函數(shù)中實現(xiàn):%@, %@", self, NSStringFromSelector(_cmd));
NSLog(@"我?guī)氲膮?shù)是name:%@ nick:%@ ",name,nick);
}
//替換C函數(shù)的OC函數(shù)
-(void)test:(NSString *)agr1 agr2:(NSString *)agr2{
NSLog(@"我?guī)氲膮?shù)是name:%@ nick:%@ ",agr1,agr2);
NSLog(@"要做什么事TODO");
}
/***********1祠饺,動態(tài)方法解析(運行的時候添加實現(xiàn)方法)************/
+(BOOL)resolveInstanceMethod:(SEL)sel{
NSLog(@"第一步:處理未知消息");
NSString *selString = NSStringFromSelector(sel);
NSLog(@"調(diào)用的方法是什么:%@",selString);
if ([selString isEqualToString:@"testMethod1"]) {
// class_addMethod(self.class, sel, (IMP)IMPTestMethod1Funcation, "@:@@");
class_addMethod(self.class, sel, class_getMethodImplementation(self, @selector(test:agr2:)), "v@:@@");
}else{
NSLog(@"第一步中不是當前要研究的方法");
}
return [super resolveInstanceMethod:sel];
}
/***********2越驻,消息轉(zhuǎn)發(fā)給別的類處理(如果1中途不對方法進行處理)************/
//如果resolveInstanceMethod 無法處理消息 就會給你一次機會 讓你把這個方法分發(fā)的別的對象去處理
-(id)forwardingTargetForSelector:(SEL)aSelector{
NSLog(@"第二步:轉(zhuǎn)發(fā)消息給別的類實現(xiàn)");
NSString *selString = NSStringFromSelector(aSelector);
if ([selString isEqualToString:@"testMethodHelp:"]) {
return [[ObjectHelper alloc]init];
}else{
NSLog(@"第二步?jīng)]有轉(zhuǎn)發(fā)消息");
}
return [super forwardingTargetForSelector:aSelector];
}
/***********3,完整的消息轉(zhuǎn)發(fā) (1 和 2 都不處理這個消息,就把這個消息封裝出來缀旁,實現(xiàn)完整轉(zhuǎn)發(fā))************/
-(void)forwardInvocation:(NSInvocation *)anInvocation{
NSLog(@"第三步:最后的完全轉(zhuǎn)發(fā)");
if([ObjectHelper instancesRespondToSelector:anInvocation.selector]){
ObjectHelper *helper = [[ObjectHelper alloc]init];
[anInvocation invokeWithTarget:helper];
}
}
//簽名這個方法
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
NSMethodSignature *signature = [super methodSignatureForSelector:aSelector];
if (!signature) {
if([ObjectHelper instancesRespondToSelector:aSelector]){
signature = [ObjectHelper instanceMethodSignatureForSelector:aSelector];
}
}
return signature;
}
運行結(jié)果:
2016-11-02 11:26:47.638 ObRunTime[41985:8436765] 第一步:處理未知消息
2016-11-02 11:26:47.638 ObRunTime[41985:8436765] 調(diào)用的方法是什么:testMethodInvork
2016-11-02 11:26:47.638 ObRunTime[41985:8436765] 第一步中不是當前要研究的方法
2016-11-02 11:26:47.639 ObRunTime[41985:8436765] 第二步:轉(zhuǎn)發(fā)消息給別的類實現(xiàn)
2016-11-02 11:26:47.639 ObRunTime[41985:8436765] 第二步?jīng)]有轉(zhuǎn)發(fā)消息
2016-11-02 11:26:47.639 ObRunTime[41985:8436765] 第一步:處理未知消息
2016-11-02 11:26:47.639 ObRunTime[41985:8436765] 調(diào)用的方法是什么:testMethodInvork
2016-11-02 11:26:47.640 ObRunTime[41985:8436765] 第一步中不是當前要研究的方法
2016-11-02 11:26:47.640 ObRunTime[41985:8436765] 第三步:最后的完全轉(zhuǎn)發(fā)
2016-11-02 11:26:47.640 ObRunTime[41985:8436765] 天啊记劈,消息轉(zhuǎn)發(fā)了
上面這個例子能很清楚的看到,消息轉(zhuǎn)發(fā)的三個步驟并巍,是不是很爽目木,哈哈!
下面舉一個實際的開發(fā)場景懊渡,大家可以看到這篇博客:http://kittenyang.com/forwardinvocation/
問題描述刽射,看博客就行坎吻,簡單的一句話就是八秃,UIScorllView的委托Delegate,能不能讓兩個或更多的實例同時相應(yīng)风范!
我們就可以用到消息轉(zhuǎn)發(fā)機制肾档,讓多個對象同時響應(yīng)Delegate 摹恰,為此我們創(chuàng)建一個Delegate分發(fā)類DelegateRouter.h
,讓它響應(yīng)目標delegate阁最,因為它沒有實現(xiàn)delegate戒祠,所以通過消息轉(zhuǎn)發(fā) 分發(fā)給目標1:ViewController
和 目標2:SecondDelegate
DelegateRouter.h
文件如下:
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import "ViewController.h"
#import "SecondDelegate.h"
@interface DelegateRouter : NSObject
@property (weak,nonatomic) ViewController *vcDelegate;
@property (strong,nonatomic) SecondDelegate *secDelegate;
@end
DelegateRouter.m
文件如下:
#import "DelegateRouter.h"
@implementation DelegateRouter
-(BOOL)respondsToSelector:(SEL)aSelector{
// NSString *test = NSStringFromSelector(aSelector);
// NSLog(@"響應(yīng)方法:%@",test);
if ([self.vcDelegate respondsToSelector:aSelector] ||[self.secDelegate respondsToSelector:aSelector]) {
return YES;
}else{
return NO;
}
}
//+(BOOL)resolveInstanceMethod:(SEL)sel{
// return [super resolveInstanceMethod:sel];
//}
-(void)forwardInvocation:(NSInvocation *)anInvocation{
if ([self.vcDelegate respondsToSelector:anInvocation.selector]) {
[anInvocation invokeWithTarget:self.vcDelegate];
}
if ([self.secDelegate respondsToSelector:anInvocation.selector]) {
[anInvocation invokeWithTarget:self.secDelegate];
}
}
//方法簽名就是 配置一段方法字符串和參數(shù)的唯一性,不是和類進行綁定
-(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
NSMethodSignature *methodSignature = [super methodSignatureForSelector:aSelector];
if (!methodSignature) {
NSMethodSignature *firstMethodSignature = [self.vcDelegate methodSignatureForSelector:aSelector];
NSMethodSignature *secondMethodSignature = [self.secDelegate methodSignatureForSelector:aSelector];
if (firstMethodSignature) {
NSLog(@"注冊1");
methodSignature = firstMethodSignature;
}else if (secondMethodSignature){
NSLog(@"注冊2");
methodSignature = secondMethodSignature;
}
return methodSignature;
}
return methodSignature;
}
@end
這個需要強調(diào):
- 我需要路由類
DelegateRouter.h
響應(yīng)我需要委托的方法速种,所以加上判斷-(BOOL)respondsToSelector:(SEL)aSelector
姜盈,只有這個返回YES 這個對象才響應(yīng)這個方法,才會調(diào)用【object sendmsg】配阵,如果沒有實現(xiàn)方法馏颂,才會走消息的轉(zhuǎn)發(fā)流程。這里我只需要處理ViewController
和SecondDelegate
里的指定委托棋傍,所以我加了判斷救拉,別的我直接不讓它響應(yīng),他就無法轉(zhuǎn)發(fā)瘫拣。 -
-(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
簽名方法亿絮,不針對類,只要你這個類實現(xiàn)了你要執(zhí)行的方法麸拄,都可以簽名這個方法派昧,這里實現(xiàn)加個if~else
其實只會執(zhí)行注冊1
我們的兩個響應(yīng)委托的地方,一個是ViewController
中
#pragma mark - UIScrollViewDelegate
-(void)scrollViewDidScroll:(UIScrollView *)scrollView{
NSLog(@"我在第一出響應(yīng)delegate");
}
一個是SecondDelegate類中
@implementation SecondDelegate
-(void)scrollViewDidScroll:(UIScrollView *)scrollView{
NSLog(@"我在第二次響應(yīng)Delegate");
}
我們再給ScrollViewDelegate 賦值的時候就選擇賦給'DelegateRouter'
delegateRouter = [[DelegateRouter alloc]init];
delegateRouter.vcDelegate = self;
delegateRouter.secDelegate = [[SecondDelegate alloc]init];
UIScrollView *scrollView = [[UIScrollView alloc]initWithFrame:self.view.bounds];
scrollView.delegate = delegateRouter;
scrollView.backgroundColor = [UIColor orangeColor];
scrollView.contentSize = CGSizeMake(CGRectGetWidth(self.view.frame), 700);
[self.view addSubview:scrollView];
這里需要注意拢切,你響應(yīng)的Delegate必須是各全局或者靜態(tài)變量蒂萎,不然無法執(zhí)行Delegate。
執(zhí)行控制臺輸出:
2016-11-02 16:08:55.445 ObRunTime[42837:8844820] 注冊1
2016-11-02 16:08:55.445 ObRunTime[42837:8844820] 我在第一出響應(yīng)delegate
2016-11-02 16:08:55.446 ObRunTime[42837:8844820] 我在第二次響應(yīng)Delegate
2016-11-02 16:08:55.465 ObRunTime[42837:8844820] 注冊1
2016-11-02 16:08:55.465 ObRunTime[42837:8844820] 我在第一出響應(yīng)delegate
2016-11-02 16:08:55.465 ObRunTime[42837:8844820] 我在第二次響應(yīng)Delegate
2016-11-02 16:08:55.496 ObRunTime[42837:8844820] 注冊1
2016-11-02 16:08:55.496 ObRunTime[42837:8844820] 我在第一出響應(yīng)delegate
2016-11-02 16:08:55.497 ObRunTime[42837:8844820] 我在第二次響應(yīng)Delegate
2016-11-02 16:08:55.514 ObRunTime[42837:8844820] 注冊1
兩個類同時響應(yīng)多個淮椰,同理五慈,你也可以弄更多的對象纳寂!
以上代碼完整版下載:鏈接: http://pan.baidu.com/s/1kVdOizH 密碼: ijtv
博客參照:http://southpeak.github.io/2014/11/03/objective-c-runtime-3/
自己理解,歡迎板磚P豪埂1形摺!聪轿!哈哈R巍!陆错!