NSInvocation详解

iOS方法调用有两种,一种是 performSelector:withObject;再一种就是NSInvocation,今天咱们介绍一下NSInvocation
performSelector:withObject; :这个我们在这里就不在这里讨论 着重讨论NSinvocation,我也是在学习消息转发机制的时候了解到的,改天把消息转发机制也详尽贴出来。写这个的目的也是为了更好的了解消息转发机制

感谢: https://my.oschina.net/u/2340880/blog/398552 的帮助

NSInvocation:是一种消息处理机制,和performSelector:withObject;能达到一样的效果,不过不一样的有两点:
performSelector:withObject;参数只能一个,而NSInvocation可以接收多个消息

  • performSelector:withObject;的如果有返回值,返回参数是传入SEL的方法返回的参数,但是NSInvocation 不会返回SEL的参数,而是会返回自己传入设定的返回值。
官方描述

An NSInvocation is an Objective-C message rendered static, that is, it is an action turned into an object. NSInvocation objects are used to store and forward messages between objects and between applications, primarily by NSTimer objects and the distributed objects system.

(英语渣请见谅,只能说我自己的理解):NSInvocation说用来处理对象和消息的媒介,让对象的方法得以实现。

注意:官网还说了,这个类不能用alloc/init创建这个对象,这也就是我们平时的好习惯了,我们创建一个方法的时候通常用苹果建议的标签方法创建

不带参数的例子


// 拿到方法
SEL myMethod = @selector(run);
// 只要那这一种形式
NSMethodSignature *signature = [[self class] instanceMethodSignatureForSelector:@selector(run)];
// 通过签名初始化,设定未来处理方法的样子
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
// 设置准备要处理的方法
[invocation setSelector:myMethod];
// 设置要处理这个方法的对象
invocation.target = self;
// 执行
[invocation invoke];

值得注意的是NSMethodSignature是一个方法信号的形式,这里的目的是我们要拿到我们未来调用方法的“样子”,而不是那个方法,并且当我们如果用alloc/init创建这个方法的时候它会给我们返回一个nil。以后消息转发机制也会写到

带参数的方法


// 设定方法的样子
SEL myMethod = @selector(run:name2:name3:);
// 预备方法
SEL myMethod2 = @selector(run);
// 返回一个方法 如果那个方法找不到则返回nil
NSMethodSignature *signature = [[self class] instanceMethodSignatureForSelector:myMethod];
// 通过签名初始化
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
// 设置target
[invocation setSelector:myMethod];
// 设置selectro
NSString *name1 = @"小明";
NSString *name2 = @"小张";
NSArray *arr3 = @[@"1",@"32",@"12334"];
//注意:1、这里设置参数的Index 需要从2开始,因为前两个被selector和target占用。下面这样写也没有任何
[invocation setArgument:&name1 atIndex:2];
[invocation setArgument:&name2 atIndex:3];
[invocation setArgument:&arr3 atIndex:4];
invocation.target = self;
[invocation invoke];

  • 当我们如果在 [[self class] instanceMethodSignatureForSelector:myMethod]方法中,填入一个不存在的方法,会返回nil

  • [[self class] instanceMethodSignatureForSelector:myMethod2]填入方法的样子,是没有参数,或者参数过少的话,会报越界错误如下图1:

NSInvocation详解_第1张图片
图1: 数组越界

注意:

  • 传入的参数的标题,必须从2,3,4,开始计算,因为第一个argumengt第一个数据和第二个数据被处理者self和即将被调用的方法引用
  • 如果我们传入的数据多于我们的传入“方法样子”,也会报越界错误
    + 调用-(void)invoke方法,这个消息才会被执行,如果不调用将不会被执行

第二种形式


// 带返回值的invocation
SEL myMethod = @selector(run:name2:name3:);
// 确定你要传入多少个参数多可以少不行
NSMethodSignature *sig = [[self class] instanceMethodSignatureForSelector:myMethod];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:sig];
id mySelf = self;
NSString *name1 = @"小明";
NSString *name2 = @"小张";
NSArray *arr3 = @[@"1",@"32",@"12334"];
NSString *name3 = @"小宝宝";
[invocation setArgument:&mySelf atIndex:0];
[invocation setArgument:&myMethod atIndex:1];
[invocation setArgument:&name1 atIndex:2];
[invocation setArgument:&name2 atIndex:3];
[invocation setArgument:&arr3 atIndex:4];
// [invocation setArgument:&name3 atIndex:5];
NSLog(@"%d", invocation.argumentsRetained);
NSLog(@"%@",invocation.target);
NSLog(@"%@",NSStringFromSelector(invocation.selector));
// 设置target值
// 不上保留了吗?为什么要还是会消失??
[invocation invokeWithTarget:self];
NSLog(@"%@",invocation.target);
// 保留参数 和返回值
[invocation retainArguments];
NSLog(@"%d",invocation.argumentsRetained);
[invocation invoke];

  • 这样就证明了上面个理解,第一个参数和第二个参数被占用了
  • retainArguments的作用是保留参数和返回值。当文档上说,当我们未调用这个方法的时候invocation.argumentsRetained = NO,当我们调用invocation.argumentsRetained = YES;
    + 给target值附上非self属性,时得注意,target最好不要是局部变量,否则当你打印target值时也会报错,并且传入的对象也必须要有这个方法
NSInvocation详解_第2张图片
图2:第二种形式

带参数带返回值


// 返回值
SEL myMethod2 = @selector(add:name2:name3:);
SEL myMethod = @selector(run:name2:name3:);
// 你要调用什么样的方法你就要给什么样的 样式,可以多不可以少
// 你设置有返回值的,那么你必须要让有返回值的方法的样式
NSMethodSignature * sig = [[self class] instanceMethodSignatureForSelector:myMethod2];
NSInvocation * invocatin = [NSInvocation invocationWithMethodSignature:sig];
// [invocatin setTarget:self];
// [invocatin setSelector:myMethod2];
ViewController * view = self;
NSString *name1 = @"小明";
NSString *name2 = @"小张";
NSArray *arr3 = @[@"1",@"32",@"12334"];
NSString *name3 = @"小宝宝";
[invocatin setArgument:&view atIndex:0];
[invocatin setArgument:&myMethod2 atIndex:1];
[invocatin setArgument:&name1 atIndex:2];
[invocatin setArgument:&name2 atIndex:3];
[invocatin setArgument:&name3 atIndex:4];
[invocatin retainArguments];
NSString * c = @"3";
//我们将c的值设置为返回值
[invocatin setReturnValue: &c];
NSLog(@"c = %@",c);
NSString * d = @"2";
//取这个返回值
[invocatin getReturnValue:&d];
NSLog(@"d = %@",d);

// NSInvocation对象,是可以有返回值的,然而这个返回值,并不是其所调用函数的返回值,需要我们手动设置:
[invocatin invoke];

NSLog(@"c = %@",c);

NSLog(@"d = %@",d);

注意

  1. 如果NSMethodSignature传入方法的"形式"没有返回值,当我们调用[invocatin setReturnValue: &c];会报错误
  2. 我们得到的返回值并不是这个方法的返回值,而是我们设定的返回值。这点非常特殊

你可能感兴趣的:(NSInvocation详解)