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类型的指针 |
| ? | 未知类型(其他的情况,一般用来指函数指针 |