iOS、OSX开发iOS_RuntimeIOS

NSInvocation详解

2016-09-18  本文已影响1691人  StonesMonkey

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

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

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

官方描述

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创建这个对象,这也就是我们平时的好习惯了,我们创建一个方法的时候通常用苹果建议的标签方法创建

不带参数的例子

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

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

带参数的方法

<pre>
// 设定方法的样子
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];
</pre>

图1: 数组越界

注意:

第二种形式

<pre>
// 带返回值的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];
</pre>

图2:第二种形式

带参数带返回值

<pre>
// 返回值
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);

</pre>

注意

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

猜你喜欢

热点阅读