NSInvocation的应用(UIWebView与JS交互传递3个以上参数)
前言
在iOS开发中,可以直接调用方法的方式有两种:performSelector:withObject: 和 NSInvocation。
performSelector:withObject:使用简单,但缺点是只能传一个参数,大于2个参数就无法使用;NSInvocation就不一样,功能更加强大
NSInvocation是一个消息调用类,它包含了所有OC消息的成分:target、selector、参数以及返回值。NSInvocation可以将消息转换成一个对象,消息的每一个参数能够直接设定,而且当一个NSInvocation对象调度时返回值是可以自己设定的。一个NSInvocation对象能够重复的调度不同的目标(target),而且它的selector也能够设置为另外一个方法签名。NSInvocation遵守NSCoding协议,但是仅支持NSPortCoder编码,不支持归档型操作。
提示:在面向对象中,方法也称消息。
小试牛刀
使用步骤
-
根据方法创建签名对象(NSMethodSignature对象)
-
根据签名对象创建调用对象(NSInvocation对象)
-
设置调用对象(NSInvocation对象)的相关信息
-
调用方法
-
获取方法返回值
- 调用有参无返回值的方法
//调用有参无返回值的方法
- (void)invocation2 {
// 1. 根据方法创建签名对象sig
NSMethodSignature *sig = [[self class] instanceMethodSignatureForSelector:@selector(methodWithArg1:arg2:)];
// 2. 根据签名对象创建调用对象invocation
NSInvocation *invocation =[NSInvocation invocationWithMethodSignature:sig];
// 3. 设置调用对象的相关信息
invocation.target = self;
invocation.selector = @selector(methodWithArg1:arg2:);
NSString *name = @"SJM";
int age = 18;
// 参数必须从第2个索引开始,因为前两个已经被target和selector使用
[invocation setArgument:&name atIndex:2];
[invocation setArgument:&age atIndex:3];
// 4. 调用方法
[invocation invoke];
}
- 调用有参有返回值的方法
- (void)invocation3 {
// 1. 根据方法创建签名对象sig
NSMethodSignature *sig = [[self class] instanceMethodSignatureForSelector:@selector(methodWithArg1:arg2:arg3:)];
// 2. 根据签名对象创建调用对象invocation
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:sig];
// 3. 设置调用对象的相关信息
invocation.target = self;
invocation.selector = @selector(methodWithArg1:arg2:arg3:);
int value1 = 111;
int value2 = 999;
int value3 = 666;
[invocation setArgument:&value1 atIndex:2];
[invocation setArgument:&value2 atIndex:3];
[invocation setArgument:&value3 atIndex:4];
// 4. 调用方法
[invocation invoke];
// 5. 获取方法返回值
NSNumber *num = nil;
[invocation getReturnValue:&num];
NSLog(@"最大数为:%@",num);
}
- (NSNumber *)methodWithArg1:(int)arg1 arg2:(int)arg2 arg3:(int)arg3 {
int max1 = MAX(arg1, arg2);
int max2 = MAX(arg2, arg3);
// 返回最大数
return @(MAX(max1, max2));
}
总结
- 可以利用上边的例子接和官方demo 对NSObject写个分类
2.js和UIwebView交互的时候可以随心所欲传递N个参数
#pragma mark - <UIWebViewDelegate>
/**
* 通过这个方法完成JS调用OC
* JS和OC交互的第三方框架:WebViewJavaScriptBridge
*/
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
// url == xmg://sendMessage_?200
NSString *url = request.URL.absoluteString;
NSString *scheme = @"xmg://";
if ([url hasPrefix:scheme]) {
// 获得协议后面的路径 path == sendMessage_number2_?200&300
NSString *path = [url substringFromIndex:scheme.length];
// 利用?切割路径
NSArray *subpaths = [path componentsSeparatedByString:@"?"];
// 方法名 methodName == sendMessage:number2:
NSString *methodName = [[subpaths firstObject] stringByReplacingOccurrencesOfString:@"_" withString:@":"];
// 参数 200&300
NSArray *params = nil;
if (subpaths.count == 2) {
params = [[subpaths lastObject] componentsSeparatedByString:@"&"];
}
// 调用
[self performSelector:NSSelectorFromString(methodName) withObjects:params];
return NO;
}
NSLog(@"想加载其他请求,不是想调用OC的方法");
return YES;
}
#import "NSObject+MFInvocation.h"
@implementation NSObject (MFInvocation)
- (id)performSelector:(SEL)aSelector withArguments:(NSArray *)arguments {
// if (aSelector == nil) return nil;
NSMethodSignature *signature = [[self class] instanceMethodSignatureForSelector:aSelector];
if (signature == nil ) {
[NSException raise:@"牛逼的错误" format:@"%@方法找不到", NSStringFromSelector(aSelector)];
//生产环境根据情况选择注销 当aSelector为空的时候会崩溃到这里
}
// NSInvocation : 利用一个NSInvocation对象包装一次方法调用(方法调用者、方法名、方法参数、方法返回值)
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
invocation.target = self;
invocation.selector = aSelector;
// 设置参数 invocation 有2个隐藏参数,所以 argument 从2开始
// 除self、_cmd以外的参数个数
if ([arguments isKindOfClass:[NSArray class]]) {
NSInteger count = MIN(arguments.count, signature.numberOfArguments - 2);
for (int i = 0; i < count; i++) {
id object = arguments[i];
if ([object isKindOfClass:[NSNull class]]) continue;
const char *type = [signature getArgumentTypeAtIndex:2 + i];
// 需要做参数类型判断然后解析成对应类型,这里默认所有参数均为OC对象
if (strcmp(type, "@") == 0) {
id argument = arguments[i];
[invocation setArgument:&argument atIndex:2 + i];
}
}
}
//调用方法
[invocation invoke];
// 获取返回值
id returnVal = nil;
// if (signature.methodReturnLength) { // 有返回值类型,才去获得返回值
// [invocation getReturnValue:&returnVal];
// }
if (strcmp(signature.methodReturnType, "@") == 0) {
[invocation getReturnValue:&returnVal];
}
// 需要做返回类型判断。比如返回值为常量需要包装成对象,这里仅以最简单的`@`为例
return returnVal;
}
@end
网友评论