問題:已知ClassA,有一個(gè)私有方法test,通過映射的方式授翻,調(diào)用私有方法test。
我的觀點(diǎn)
寫這個(gè)問題答案之前肋拔,我想說的是锈津,我的邏輯思維中,直覺還是不靈敏凉蜂,好多事情琼梆,都想當(dāng)然了,我覺得思維縝密窿吩,直覺很重要茎杂,這個(gè)是天賦,還有就是習(xí)慣纫雁。
像我這樣直覺不靈敏的煌往,遇見問題,一定要仔細(xì)想想,是否有考慮不周的地方刽脖,這個(gè)要養(yǎng)成這樣的習(xí)慣羞海。
另一種觀點(diǎn)
另外跟大洋洋討論這個(gè)題的時(shí)候,又衍生出來了另一個(gè)問題曲管,我覺得也應(yīng)該記錄下來却邓,好好琢磨琢磨。因?yàn)橛懻撨@個(gè)問題的時(shí)候院水,他也沒有答完整腊徙,他總結(jié)說:“1. 現(xiàn)在回答這類問題,總感覺答不上檬某,2. 感覺iOS會(huì)啥技術(shù)撬腾,也說不出來啥體系,感覺自己的知識(shí)體系樹沒有建立起來恢恼,就是遇見問題解決問題民傻。” 我也有這樣的情況场斑,我覺得我以前的開發(fā)饰潜,就是遇見問題,解決問題和簸,再遇見問題,在解決問題碟刺,得過且過锁保,后來又有了改變,開始注重整體的結(jié)構(gòu)代碼機(jī)構(gòu)半沽,開始喜歡學(xué)習(xí)設(shè)計(jì)模式爽柒,學(xué)習(xí)技巧,因?yàn)檫@些經(jīng)驗(yàn)可以有效的避免一些問題的發(fā)生者填,讓代碼變得更加優(yōu)雅浩村,好維護(hù)好修改,其實(shí)還是遇見問題解決問題占哟。
解決這個(gè)問題
解決這個(gè)問題心墅,要注意的幾個(gè)點(diǎn):ClassA ,私有方法test,映射榨乎,調(diào)用私有方法
剛開始的想法是:
ClassA 這個(gè)類的權(quán)限問題怎燥,是否能夠引用,如果是public的蜜暑,可以引用可以直接new對(duì)象铐姚,如果是private或者是protect,引用不了就可以通過NSClassFromString來獲得這個(gè)ClassA的類型來進(jìn)行處理肛捍。
私有方法我想簡單了隐绵,這里我開始并沒有考慮到test是否有參數(shù)和返回值的問題
映射就是通過類名找到類的類型之众,為后續(xù)初始化提供準(zhǔn)備
調(diào)用私有方法,msg_send方法依许,或者performForSelector方法棺禾。
#import "A.h"
@implementation A
- (void)test {
NSLog(@"可以調(diào)用私有方法");
}
@end
A * a = [[A alloc] init];
// [a performSelector:NSSelectorFromString(@"test")];
[a performSelector:@selector(test)];
從上面打印結(jié)果可以看出,通過performSelector方法是可以調(diào)用test私有方法的悍手,但是會(huì)有警告帘睦。
帶有參數(shù)甚至是多個(gè)參數(shù)的
#import "A.h"
@implementation A
- (void)test:(NSString *)c1 {
NSLog(@"可以調(diào)用私有方法%@",c1);
}
@end
A * a = [[A alloc] init];
[a performSelector:@selector(test:) withObject:@"加了一個(gè)參數(shù)"];
這個(gè)思路有個(gè)問題不好解決
test這個(gè)方法,沒參數(shù)坦康,有參數(shù)竣付,有多個(gè)參數(shù),通過performSelector這個(gè)api,沒法做出抽象滞欠,只能通過判斷這幾種情況古胆,去硬處理。
從performSelector的這幾個(gè)api的設(shè)計(jì)來看筛璧,猜測運(yùn)行時(shí)的objc_msgSend方法應(yīng)該會(huì)有對(duì)這幾個(gè)perfomeSelector方法的抽象逸绎。
既然用運(yùn)行時(shí),那么就先用運(yùn)行時(shí)的方法夭谤,檢查是否有參數(shù)的問題棺牧。
這個(gè)地方我在做的時(shí)候,有點(diǎn)卡頓朗儒,卡頓的原因是想通過運(yùn)行時(shí)的方法颊乘,通過方法名找到方法,再監(jiān)測這個(gè)方法里面有沒有參數(shù)醉锄,有參數(shù)參數(shù)的類型是什么樣的乏悄。但是發(fā)現(xiàn)運(yùn)行時(shí)的方法,并沒有提供我這個(gè)思路的api恳不。
這個(gè)地方需要換一個(gè)思路檩小,私有方法是不公開的,test這個(gè)私有方法是怎么得到的烟勋,一種情況是能看到源文件规求,也就是.m文件是可以看到的,所以可以直接確定是否有返回值神妹,是否有參數(shù)颓哮。另一種情況是看不到.m文件,看不到文件的鸵荠,就需要通過運(yùn)行時(shí)方法冕茅,打印這個(gè)類中的所有方法,然后找到想要用的方法,打印的時(shí)候姨伤,也是可以把方法的參數(shù)哨坪,返回值類型打印出來的。
所以說乍楚,test這個(gè)方法是人為選擇出來的当编,也就是說我們要調(diào)用這個(gè)方法的時(shí)候,會(huì)明確知道這個(gè)方法是不是有返回值徒溪,是不是有參數(shù)忿偷,有幾個(gè)參數(shù)等等信息。
假設(shè)有5個(gè)參數(shù)的時(shí)候怎么調(diào)用
#import "A.h"
@implementation A
- (void)test:(NSString *)c1 c2:(NSString *)c2 c3:(NSString *)c3 c4:(NSString *)c4 c5:(NSString *)c5 {
NSLog(@"私有方法含有多個(gè)參數(shù)%@,%@,%@,%@,%@",c1,c2,c3,c4,c5);
}
@end
//調(diào)用代碼
Class A = NSClassFromString(@"A");
id a = [[A alloc] init];
((void (*)(id,SEL,id,id,id,id,id))objc_msgSend)(a,@selector(test:c2:c3:c4:c5:),@"1",@"2",@"3",@"4",@"5");
輸出結(jié)果
如果有返回值的怎么調(diào)用
#import "A.h"
@implementation A
- (NSString *)test:(NSString *)c1 c2:(NSString *)c2 c3:(NSString *)c3 c4:(NSString *)c4 c5:(NSString *)c5 {
return [NSString stringWithFormat:@"私有方法含有多個(gè)參數(shù)%@,%@,%@,%@,%@",c1,c2,c3,c4,c5];
}
@end
//調(diào)用代碼
Class A = NSClassFromString(@"A");
id a = [[A alloc] init];
NSString * b = ((NSString * (*)(id,SEL,id,id,id,id,id))objc_msgSend)(a,@selector(test:c2:c3:c4:c5:),@"1",@"2",@"3",@"4",@"5");
NSLog(@"%@",b);
輸出結(jié)果
歸納總結(jié)
已知類名臊泌,調(diào)用私有方法鲤桥,比較好的方法是
- 創(chuàng)建類的時(shí)候,用反射機(jī)制渠概,得到類class, 然后實(shí)例化茶凳。
- 通過實(shí)例對(duì)象,調(diào)用私有方法的時(shí)候播揪,要考慮私有方法是否有有返回值贮喧,是否有參數(shù),根據(jù)不同的情況進(jìn)行處理猪狈。
既然上面的方式可以成功箱沦,那也也可以直接用函數(shù)指針調(diào)用
#import "A.h"
@implementation A
- (void)test {
NSLog(@"可以調(diào)用私有方法");
}
@end
//調(diào)用方法
Class A = NSClassFromString(@"A");
id a = [[A alloc] init];
IMP imp = [a methodForSelector:@selector(test)];
void (* tempFunc)(id target, SEL) = (void *)imp;
tempFunc(a, @selector(test));
只要能拿到私有方法的指針,就可以想辦法通過指針去調(diào)用雇庙。