准备工作
// 返回值id
// 外部调用, 通过target和action来唯一确认一个类里面的方法
- (id)performTarget:(NSString *)targetName action:(NSString *)actionName param:(NSDictionary *)para;
- target-action 两个参数来确定target 和 action , para 你需要传递的参数
接口实现
- (id)performTarget:(NSString *)targetName action:(NSString *)actionName param:(NSDictionary *)para {
// 这个目标的类名字符串
NSString *targetClassString = [NSString stringWithFormat:@"RYLSJTA_%@",targetName];
NSString *actionMethondString = [NSString stringWithFormat:@"action_%@:",actionName];
Class targetClass = NSClassFromString(targetClassString);
NSObject *target = [[targetClass alloc] init];
SEL action = NSSelectorFromString(actionMethondString);
// 判断
if ([target respondsToSelector:action]) {
return [self safePerformAction:action target:target param:para];
} else {
SEL action = NSSelectorFromString(@"notFound:");
if ([target respondsToSelector:action]) {
return [self safePerformAction:action target:target param:para];
} else {
return nil;
}
}
return nil;
}
- 返回值使用ID类型是因为我们可能在模块中得到的是一个登录的控制器,而不是其它类型。
- 如果没有需要的类或者实现的方法我们需要做额外的处理,防止崩溃
- 如果你有参数传递需要处理的话 需要在 action_%@: 这里加上个冒号不然没有反应
传递的参数处理(核心代码)
// 1.通过对象调用指定的方法
// 2.传参
- (id)safePerformAction:(SEL)action target:(NSObject *)target param:(NSDictionary *)para {
NSMethodSignature *methodSig = [target methodSignatureForSelector:action];
if (methodSig == nil) {
return nil;
}
// 方法签名的返回值
const char *retType = [methodSig methodReturnType];
// id 是可以返回任意对象 所以 我们单独处理基本变量. NSInteger Bool Void...
if (strcmp(retType, @encode(NSInteger)) == 0) {
//通过方法调用者创建方法签名;此方法是NSObject 的方法
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSig];
// 为什么传2? 前面0和1这两个位置已经被target和action给占用了.
[invocation setArgument:¶ atIndex:2];
[invocation setTarget:target];
[invocation setSelector:action];
// 执行invocation
[invocation invoke];
NSInteger result = 0;
[invocation getReturnValue:&result];
return @(result);
}
if (strcmp(retType, @encode(BOOL)) == 0) {
// 通过方法调用者创建方法签名;此方法是NSObject 的方法
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSig];
// 为什么传2? 前面0和1这两个位置已经被target和action给占用了.
[invocation setArgument:¶ atIndex:2];
[invocation setTarget:target];
[invocation setSelector:action];
// 执行invocation
[invocation invoke];
NSInteger result = 0;
[invocation getReturnValue:&result];
return @(result);
}
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
return [target performSelector:action withObject:target withObject:para];
#pragma clang diagnostic pop
}
- setArgument 设置参数一定要从2开始,因为里面模式是self和_cmd 2个给占用了
- 如果有多个参数的话 就atIndex:3,atIndex:4 这种形式就可以
- strcmp(retType, @encode(BOOL) 转码判断,这里的类型可以根据情况设置
- 使用push pop 是为了消除警告,因为我们在上面已经处理过了,我们已经知道了类型,所以这里消除警告即可
最终开发想要的效果
Router Demo 测试结果展示
RYLSJRouter下载链接
网友评论