NSInvocation

NSInvocation

一、概念

在 iOS中可以直接调用某个对象的消息方式有两种:

  • performSelector:withObject(两个以上参数使用比较麻烦)
  • NSInvocation(参数个数没有限制)

    二、使用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    - (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 = nil
    if (!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;
    }

输出

1
2
3
4
5
CTMediator[12211:1876597] performSelector调用了方法
CTMediator[12211:1876597] NSInvocation调用了方法
CTMediator[12211:1876597] MyLog1:2:3
CTMediator[12211:1876597] 返回类型为普通类型
CTMediator[12211:1876597] 返回值是:6

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