NSInvocation
一、概念
在 iOS中可以直接调用某个对象的消息方式有两种:
- performSelector:withObject(两个以上参数使用比较麻烦)
- NSInvocation(参数个数没有限制)
二、使用
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990- (void)viewDidLoad {[super viewDidLoad];[self performSelector:@selector(sendMessage:) withObject:@"performSelector"];//没有返回值[self createInvocation];//有返回值[self returnValueInvocation];}- (void)createInvocation{//NSInvocation;用来包装方法和对应的对象,它可以存储方法的名称,对应的对象,对应的参数,/*NSMethodSignature:签名:再创建NSMethodSignature的时候,必须传递一个签名对象,签名对象的作用:用于获取参数的个数和方法的返回值*///创建签名对象的时候不是使用NSMethodSignature这个类创建,而是方法属于谁就用谁来创建NSMethodSignature *signature = [ViewController instanceMethodSignatureForSelector:@selector(sendMessage:)];//1、创建NSInvocation对象NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];invocation.target = self;//invocation中的方法必须和签名中的方法一致。invocation.selector = @selector(sendMessage:);/*第一个参数:需要给指定方法传递的值第一个参数需要接收一个指针,也就是传递值的时候需要传递地址*///第二个参数:需要给指定方法的第几个参数传值NSString *number = @"NSInvocation";//注意:设置参数的索引时不能从0开始,因为0已经被self占用,1已经被_cmd占用[invocation setArgument:&number atIndex:2];//2、调用NSInvocation对象的invoke方法//只要调用invocation的invoke方法,就代表需要执行NSInvocation对象中制定对象的指定方法,并且传递指定的参数[invocation invoke];}- (void)returnValueInvocation{SEL myMethod = @selector(returnMessage:parm:parm:);NSMethodSignature *sig = [[self class] instanceMethodSignatureForSelector:myMethod];NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:sig];[invocation setTarget:self];[invocation setSelector:myMethod];int a=1;int b=2;int c=3;[invocation setArgument:&a atIndex:2];[invocation setArgument:&b atIndex:3];[invocation setArgument:&c atIndex:4];[invocation invoke];//获取返回值类型const char * returnValueType = sig.methodReturnType;//声明一个返回值变量 (arc模式下,getReturnValue:仅仅是从invocation的返回值拷贝到指定的内存地址,如果返回值是一个NSObject对象的话,是没有处理起内存管理的。而我们在定义resultSet时使用的是__strong类型的指针对象,arc就会假设该内存块已被retain(实际没有),当resultSet出了定义域释放时,导致该crash。假如在定义之前有赋值的话,还会造成内存泄露的问题。)__unsafe_unretained NSObject *returnValue = nil;//如果没有返回值,也就是消息声明为void,那么returnValue = nilif (!strcmp(returnValueType, @encode(void))) {NSLog(@"没有返回值,即返回值类型为void");returnValue = nil;}else if (!strcmp(returnValueType, @encode(id))){//如果返回值为对象,那么为变量赋值NSLog(@"返回值类型为对象");[invocation getReturnValue:&returnValueType];}else {//如果返回值为普通类型,如NSInteger, NSUInteger ,BOOL等NSLog(@"返回类型为普通类型");//首先获取返回值长度NSUInteger returnValueLenth = sig.methodReturnLength;//根据长度申请内存void *retValue = (void *)malloc(returnValueLenth);//为retValue赋值[invocation getReturnValue:retValue];if (!strcmp(returnValueType, @encode(BOOL))) {returnValue = [NSNumber numberWithBool:*((BOOL *)retValue)];}else if (!strcmp(returnValueType, @encode(NSInteger))){returnValue = [NSNumber numberWithInteger:*((NSInteger *) retValue)];}else if (!strcmp(returnValueType, @encode(int))){returnValue = [NSNumber numberWithInt:*((int *) retValue)];}// 、、、其他类型}NSLog(@"返回值是:%@", returnValue);}- (void)sendMessage:(NSString*)message{NSLog(@"%@调用了方法",message);}- (int)returnMessage:(int)a parm:(int)b parm:(int)c{NSLog(@"MyLog%d:%d:%d",a,b,c);return a+b+c;}
输出
|
|
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类型的指针 |
? | 未知类型(其他的情况,一般用来指函数指针 |