美文网首页ios 学习
iOS 调用私有方法的问题

iOS 调用私有方法的问题

作者: 花生儿 | 来源:发表于2019-09-29 09:56 被阅读0次

    问题:已知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

    归纳总结
    已知类名,调用私有方法,比较好的方法是

    1. 创建类的时候,用反射机制,得到类class, 然后实例化。
    2. 通过实例对象,调用私有方法的时候,要考虑私有方法是否有有返回值,是否有参数,根据不同的情况进行处理。

    既然上面的方式可以成功,那也也可以直接用函数指针调用

    #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));
    

    只要能拿到私有方法的指针,就可以想办法通过指针去调用。

    相关文章

      网友评论

        本文标题:iOS 调用私有方法的问题

        本文链接:https://www.haomeiwen.com/subject/nmdwuctx.html