在 iOS中可以直接调用某个对象的消息方式有两种:
一种是performSelector:withObject 另一种就是NSInvocation
第一种方式比较简单,能完成简单的调用。但是对于>2个的参数或者有返回值的处理,那就需要做些额外工作才能搞定。那么在这种情况下,我们就可以使用NSInvocation来进行这些相对复杂的操作。
1、
DZPerson.h:
#import <Foundation/Foundation.h> @interface DZPerson : NSObject @end
DZPerson.m
#import "DZPerson.h" @implementation DZPerson - (void) showName:(NSString *) name { NSLog(@"name : %@", name); } - (void) showName:(NSString *) name andAge:(NSUInteger) age { NSLog(@"name : %@, age : %lu", name, (unsigned long)age); } - (NSString *) getAgeAndNameWithName:(NSString *) name { NSString * str = [NSString stringWithFormat:@"age : 15, name : %@", name]; NSLog(@"%@", str); return str; } - (NSInteger) getAge { return 1970; } @end
代码中有详细的注释:
// // ViewController.m // 001-nsinvocation // // #import "ViewController.h" #import "DZPerson.h" @interface ViewController () @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. self.view.backgroundColor = [UIColor whiteColor]; DZPerson * p1 = [[DZPerson alloc] init]; //1、一个参数 NSMethodSignature * methodSignature1 = [[p1 class] instanceMethodSignatureForSelector:@selector(showName:)]; NSInvocation * invocation1 = [NSInvocation invocationWithMethodSignature:methodSignature1]; [invocation1 setTarget:p1]; [invocation1 setSelector:@selector(showName:)]; NSString * name = @"ldz"; //如果此消息有参数需要传入,那么就需要按照如下方法进行参数设置,需要注意的是,atIndex的下标必须从2开始。原因为:0 1 两个参数已经被target 和selector占用 [invocation1 setArgument:&name atIndex:2]; //防止参数被释放掉 [invocation1 retainArguments]; //调用 [invocation1 invoke]; //2、两个参数 NSMethodSignature * methodSignature2 = [[p1 class] instanceMethodSignatureForSelector:@selector(showName:andAge:)]; NSInvocation * invocation2 = [NSInvocation invocationWithMethodSignature:methodSignature2]; [invocation2 setTarget:p1]; [invocation2 setSelector:@selector(showName:andAge:)]; NSUInteger age = 18; [invocation2 setArgument:&name atIndex:2]; [invocation2 setArgument:&age atIndex:3]; [invocation2 retainArguments]; [invocation2 invoke]; //3、有返回值的情况 NSMethodSignature * methodSignature3 = [[p1 class] instanceMethodSignatureForSelector:@selector(getAgeAndNameWithName:)]; NSInvocation * invocation3 = [NSInvocation invocationWithMethodSignature:methodSignature3]; [invocation3 setTarget:p1]; [invocation3 setSelector:@selector(getAgeAndNameWithName:)]; [invocation3 setArgument:&name atIndex:2]; [invocation3 retainArguments]; [invocation3 invoke]; //获取返回值类型 const char * returnValueType = methodSignature3.methodReturnType; NSLog(@"returnValueType : %s", returnValueType); //声明一个返回值变量 id returnValue; //如果没有返回值,也就是消息声明为void,那么returnValue = nil if (!strcmp(returnValueType, @encode(void))) { NSLog(@"没有返回值,即返回值类型为void"); returnValue = nil; }else if (!strcmp(returnValueType, @encode(id))){ //如果返回值为对象,那么为变量赋值 NSLog(@"返回值类型为对象"); [invocation3 getReturnValue:&returnValue]; }else { //如果返回值为普通类型,如NSInteger, NSUInteger ,BOOL等 NSLog(@"返回类型为普通类型"); //首先获取返回值长度 NSUInteger returnValueLenth = methodSignature3.methodReturnLength; //根据长度申请内存 void * retValue = (void *)malloc(returnValueLenth); //为retValue赋值 [invocation3 getReturnValue:retValue]; if (!strcmp(returnValueType, @encode(BOOL))) { returnValue = [NSNumber numberWithBool:*((BOOL *)retValue)]; }else if (!strcmp(returnValueType, @encode(NSInteger))){ returnValue = [NSNumber numberWithInteger:*((NSInteger *) retValue)]; } //。。。 余下省略 } NSLog(@"返回值是:%@", returnValue); //4、返回类型为普通类型的情况 NSMethodSignature * methodSignature4 = [[p1 class] instanceMethodSignatureForSelector:@selector(getAge)]; NSInvocation * invocation4 = [NSInvocation invocationWithMethodSignature:methodSignature3]; [invocation4 setTarget:p1]; [invocation4 setSelector:@selector(getAge)]; [invocation4 invoke]; //获取返回值类型 const char * returnValueType4 = methodSignature4.methodReturnType; NSLog(@"returnValueType : %c", *returnValueType4); //声明一个返回值变量 id returnValue4; //如果没有返回值,也就是消息声明为void,那么returnValue = nil if (!strcmp(returnValueType4, @encode(void))) { NSLog(@"没有返回值,即返回值类型为void"); returnValue4 = nil; }else if (!strcmp(returnValueType4, @encode(id))){ //如果返回值为对象,那么为变量赋值 NSLog(@"返回值类型为对象"); [invocation4 getReturnValue:&returnValue4]; }else { //如果返回值为普通类型,如NSInteger, NSUInteger ,BOOL等 NSLog(@"返回类型为普通类型"); //首先获取返回值长度 NSUInteger returnValueLenth4 = methodSignature4.methodReturnLength; //根据长度申请内存 void * retValue4 = (void *)malloc(returnValueLenth4); //为retValue赋值 [invocation4 getReturnValue:retValue4]; if (!strcmp(returnValueType4, @encode(BOOL))) { returnValue4 = [NSNumber numberWithBool:*((BOOL *)retValue4)]; }else if (!strcmp(returnValueType4, @encode(NSInteger))){ returnValue4 = [NSNumber numberWithInteger:*((NSInteger *) retValue4)]; } //。。。 余下省略 } NSLog(@"返回值是:%@", returnValue4); } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } @end
输出:
2016-02-16 14:23:00.093 001-nsinvocation[2766:96619] name : ldz 2016-02-16 14:23:00.094 001-nsinvocation[2766:96619] name : ldz, age : 18 2016-02-16 14:23:00.094 001-nsinvocation[2766:96619] age : 15, name : ldz 2016-02-16 14:23:00.094 001-nsinvocation[2766:96619] returnValueType : @ 2016-02-16 14:23:00.094 001-nsinvocation[2766:96619] 返回值类型为对象 2016-02-16 14:23:00.094 001-nsinvocation[2766:96619] 返回值是:age : 15, name : ldz 2016-02-16 14:23:00.094 001-nsinvocation[2766:96619] returnValueType : q 2016-02-16 14:23:00.095 001-nsinvocation[2766:96619] 返回类型为普通类型 2016-02-16 14:23:00.095 001-nsinvocation[2766:96619] 返回值是:1970
3、其中Objective-C类型编码为:
编码 | 含义 |
c | char |
i | int |
s | short |
l | long 在64位程序中,l为32位 |
q | long long |
C(大写) | unsigned char |
I(大写) | unsigned int |
S(大写) | unsigned short |
L | unsigned long |
Q | unsigned long long |
f | float |
d | double |
B | C++标准的bool或者C99标准的_Bool |
v | void |
* | 字符串(char *) |
@ | 对象(无论是静态指定的还是通过id引用的) |
# | 类(class) |
: | 方法选标(SEL) |
[array type] | 数组 |
{name=type...} | 结构体 |
(name=type...) | 联合体 |
bnum | num个bit的位域 |
^type | type类型的指针 |
? | 未知类型(其他的情况,一般用来指函数指针) |