问题:已知ClassA,有一个私有方法test,通过映射的方式,调用私有方法test。
我的观点
写这个问题答案之前,我想说的是,我的逻辑思维中,直觉还是不灵敏,好多事情,都想当然了,我觉得思维缜密,直觉很重要,这个是天赋,还有就是习惯。
像我这样直觉不灵敏的,遇见问题,一定要仔细想想,是否有考虑不周的地方,这个要养成这样的习惯。
另一种观点
另外跟大洋洋讨论这个题的时候,又衍生出来了另一个问题,我觉得也应该记录下来,好好琢磨琢磨。因为讨论这个问题的时候,他也没有答完整,他总结说:“1. 现在回答这类问题,总感觉答不上,2. 感觉iOS会啥技术,也说不出来啥体系,感觉自己的知识体系树没有建立起来,就是遇见问题解决问题。” 我也有这样的情况,我觉得我以前的开发,就是遇见问题,解决问题,再遇见问题,在解决问题,得过且过,后来又有了改变,开始注重整体的结构代码机构,开始喜欢学习设计模式,学习技巧,因为这些经验可以有效的避免一些问题的发生,让代码变得更加优雅,好维护好修改,其实还是遇见问题解决问题。
解决这个问题
解决这个问题,要注意的几个点:ClassA ,私有方法test,映射,调用私有方法
刚开始的想法是:
ClassA 这个类的权限问题,是否能够引用,如果是public的,可以引用可以直接new对象,如果是private或者是protect,引用不了就可以通过NSClassFromString来获得这个ClassA的类型来进行处理。
私有方法我想简单了,这里我开始并没有考虑到test是否有参数和返回值的问题
映射就是通过类名找到类的类型,为后续初始化提供准备
调用私有方法,msg_send方法,或者performForSelector方法。
#import "A.h"
@implementation A
- (void)test {
NSLog(@"可以调用私有方法");
}
@end
A * a = [[A alloc] init];
// [a performSelector:NSSelectorFromString(@"test")];
[a performSelector:@selector(test)];
image.png
从上面打印结果可以看出,通过performSelector方法是可以调用test私有方法的,但是会有警告。
带有参数甚至是多个参数的
#import "A.h"
@implementation A
- (void)test:(NSString *)c1 {
NSLog(@"可以调用私有方法%@",c1);
}
@end
A * a = [[A alloc] init];
[a performSelector:@selector(test:) withObject:@"加了一个参数"];
image.png
这个思路有个问题不好解决
test这个方法,没参数,有参数,有多个参数,通过performSelector这个api,没法做出抽象,只能通过判断这几种情况,去硬处理。
从performSelector的这几个api的设计来看,猜测运行时的objc_msgSend方法应该会有对这几个perfomeSelector方法的抽象。
既然用运行时,那么就先用运行时的方法,检查是否有参数的问题。
这个地方我在做的时候,有点卡顿,卡顿的原因是想通过运行时的方法,通过方法名找到方法,再监测这个方法里面有没有参数,有参数参数的类型是什么样的。但是发现运行时的方法,并没有提供我这个思路的api。
这个地方需要换一个思路,私有方法是不公开的,test这个私有方法是怎么得到的,一种情况是能看到源文件,也就是.m文件是可以看到的,所以可以直接确定是否有返回值,是否有参数。另一种情况是看不到.m文件,看不到文件的,就需要通过运行时方法,打印这个类中的所有方法,然后找到想要用的方法,打印的时候,也是可以把方法的参数,返回值类型打印出来的。
所以说,test这个方法是人为选择出来的,也就是说我们要调用这个方法的时候,会明确知道这个方法是不是有返回值,是不是有参数,有几个参数等等信息。
假设有5个参数的时候怎么调用
#import "A.h"
@implementation A
- (void)test:(NSString *)c1 c2:(NSString *)c2 c3:(NSString *)c3 c4:(NSString *)c4 c5:(NSString *)c5 {
NSLog(@"私有方法含有多个参数%@,%@,%@,%@,%@",c1,c2,c3,c4,c5);
}
@end
//调用代码
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");
输出结果
image.png
如果有返回值的怎么调用
#import "A.h"
@implementation A
- (NSString *)test:(NSString *)c1 c2:(NSString *)c2 c3:(NSString *)c3 c4:(NSString *)c4 c5:(NSString *)c5 {
return [NSString stringWithFormat:@"私有方法含有多个参数%@,%@,%@,%@,%@",c1,c2,c3,c4,c5];
}
@end
//调用代码
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);
输出结果
image.png
归纳总结
已知类名,调用私有方法,比较好的方法是
- 创建类的时候,用反射机制,得到类class, 然后实例化。
- 通过实例对象,调用私有方法的时候,要考虑私有方法是否有有返回值,是否有参数,根据不同的情况进行处理。
既然上面的方式可以成功,那也也可以直接用函数指针调用
#import "A.h"
@implementation A
- (void)test {
NSLog(@"可以调用私有方法");
}
@end
//调用方法
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));
只要能拿到私有方法的指针,就可以想办法通过指针去调用。
网友评论