在 iOS中可以直接调用某个对象的消息方式有两种:
一种是performSelector:withObject 另一种就是NSInvocation
第一种方式比较简单,能完成简单的调用。但是对于>2个的参数或者有返回值的处理,那就需要做些额外工作才能搞定。那么在这种情况下,我们就可以使用NSInvocation来进行这些相对复杂的操作。
1、
DZPerson.h:
#import <Foundation/Foundation.h>
@interface DZPerson : NSObject
@end
.h文件中并不需要有方法的声明
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
2、测试:
代码中有详细的注释:
//
// 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类型的指针 |
? | 未知类型(其他的情况,一般用来指函数指针) |