最近碰到一个需要,在登录异常的情况下,需要走账号验证流程,等到验证码等等验证通过了以后,就要进入要首页,但是拿到用户信息到进入首页这个过程的代码有点多,我当时是写成一个方法放在登录页面的,如果直接把所有代码都拷贝过来当然可以,但是有点麻烦,我想直接让界面退回到登录页,然后调用它的私有API,处理登录完成到进入首页的这部分逻辑。
方法一 (最简单的方法)
- (id)performSelector:(SEL)aSelector;
- (id)performSelector:(SEL)aSelector withObject:(id)object;
- (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2;
这三个方法都可以调用私有API,区别就是SEL带的参数多少的问题,后面的withobjcect就是这个selector带的参数,分别是带0个参数,带一个参数,带两个参数
例子:
#import
NS_ASSUME_NONNULL_BEGIN
@interface Person : NSObject
@end
NS_ASSUME_NONNULL_END
#import "Person.h"
@implementation Person
// 无参数
- (void)noArg
{
NSLog(@"no arg");
}
// 一个参数
- (void)oneArg:(NSString *)oneage
{
NSLog(@"one arg = %@",oneage);
}
// 两个参数
- (void)twoArg:(NSString *)arg1 arg2:(NSNumber *)arg2
{
NSLog(@"arg1 = %@,arg2 = %@",arg1,arg2);
}
@end
// 执行
Person *p = [Person new];
[p performSelector:@selector(noArg)];
[p performSelector:@selector(oneArg:) withObject:@"123"];
[p performSelector:@selector(twoArg:arg2:) withObject:@"123" withObject:@1];
// 打印
2018-07-23 12:02:27.992171+0800 privateMethod[48241:13537358] no arg
2018-07-23 12:02:27.992295+0800 privateMethod[48241:13537358] one arg = 123
2018-07-23 12:02:27.992368+0800 privateMethod[48241:13537358] arg1 = 123,arg2 = 1
至此,需求其实已经完成了,但是我记得以前看MJRefresh的时候,里面有用到msg_send()函数,理论上来说也是可以完成调用私有API的。
方法二 (msg_send())
在调用msg_send()消息发送之前,我们先打印一下这个类的所有方法看看
unsigned int count = 0;
Method *methods = class_copyMethodList([Person class], &count);
for (int i = 0; i < count; i++) {
SEL name = method_getName(methods[i]);
NSString *methodName = [NSString stringWithCString:sel_getName(name) encoding:NSUTF8StringEncoding];
NSLog(@"%@",methodName);
}
// 打印
2018-07-23 12:06:04.016795+0800 privateMethod[48309:13549676] noArg
2018-07-23 12:06:04.016921+0800 privateMethod[48309:13549676] oneArg:
2018-07-23 12:06:04.017006+0800 privateMethod[48309:13549676] twoArg:arg2:
注意到,我们直接调用msg_send(),那么调用的函数是无参的,但是我们要传递参数
* These functions must be cast to an appropriate function pointer type
* before being called.
官方给的注释说在调用前,我们需要自定义一个函数指针类型去指向它。
那么假如说我要调用第三个函数,我应该这么去做
void (*methodTwo)(id,SEL,NSString *,NSNumber *) = (void (*) (id,SEL,NSString * ,NSNumber *))objc_msgSend;
Person *p = [Person new];
methodTwo(p,@selector(twoArg:arg2:),@"123",@2);
// 打印
2018-07-23 12:15:59.093006+0800 privateMethod[48493:13719630] arg1 = 123,arg2 = 2
需求达成
拓展 (不定参数)
假设现在新添加一个不定参数的函数
- (void)print:(NSString *)firstArg, ... NS_REQUIRES_NIL_TERMINATION {
if (firstArg) {
NSLog(@"%@", firstArg);
va_list args;
NSString *arg;
va_start(args, firstArg);
while ((arg = va_arg(args,id))) {
NSLog(@"%@", arg);
}
va_end(args);
}
}
其实这个方法的方法名跟只有一个参数的方法名是一样的,还是
print:
但是在定义函数指针的时候,我们还是要用...去表示省略参数
void (*methodPrint)(id,SEL,NSString * , ...) = (void (*) (id,SEL,NSString * , ...))objc_msgSend;
methodPrint(p,@selector(print:),@"123",@"456",nil);
// 打印
2018-07-23 12:41:46.454412+0800 privateMethod[49097:13875153] 123
2018-07-23 12:41:46.454546+0800 privateMethod[49097:13875153] 456
Demo参考地址
喜欢就给个star吧