IOS NSInvocation应用与理解

简介:

IOS中有一个类型是SEL,它的作用很相似与函数指针,通过performSelector:withObject:函数可以直接调用这个消息。但是perform相关的这些函数,有一个局限性,其参数数量不能超过2个,否则要做很麻烦的处理,与之相对,NSInvocation也是一种消息调用的方法,并且它的参数没有限制。这两种直接调用对象消息的方法,在IOS4.0之后,大多被block结构所取代,只有在很老的兼容性系统中才会使用,简单用法总结如下:

一、初始化与调用

在官方文档中有明确说明,NSInvocation对象只能使用其类方法来初始化,不可使用alloc/init方法。它执行调用之前,需要设置两个方法:setSelector: 和setArgument:atIndex:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
- ( void )viewDidLoad {
     [super viewDidLoad];
     SEL myMethod = @selector(myLog);
     //创建一个函数签名,这个签名可以是任意的,但需要注意,签名函数的参数数量要和调用的一致。
     NSMethodSignature * sig  = [NSNumber instanceMethodSignatureForSelector:@selector(init)];
     //通过签名初始化
     NSInvocation * invocatin = [NSInvocation invocationWithMethodSignature:sig];
     //设置target
     [invocatin setTarget:self];
     //设置selecteor
     [invocatin setSelector:myMethod];
     //消息调用
     [invocatin invoke];
     
}
-( void )myLog{
     NSLog(@ "MyLog" );
}

注意:签名函数的参数数量要和调用函数的一致。测试后发现,当签名函数参数数量大于被调函数时,也是没有问题的。

调用多参数的方法,我们可以这样写:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
- ( void )viewDidLoad {
     [super viewDidLoad];
     SEL myMethod = @selector(myLog:parm:parm:);
     NSMethodSignature * sig  = [[self  class ] instanceMethodSignatureForSelector:myMethod];
     NSInvocation * invocatin = [NSInvocation invocationWithMethodSignature:sig];
     [invocatin setTarget:self];
     [invocatin setSelector:myMethod2];
     int  a=1;
     int  b=2;
     int  c=3;
     [invocatin setArgument:&a atIndex:2];
     [invocatin setArgument:&b atIndex:3];
     [invocatin setArgument:&c atIndex:4];
     [invocatin invoke];
}
-( void )myLog:( int )a parm:( int )b parm:( int )c{
     NSLog(@ "MyLog%d:%d:%d" ,a,b,c);
}

注意:1、这里设置参数的Index 需要从2开始,因为前两个被selector和target占用。下面这样写也没有任何问题:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
- ( void )viewDidLoad {
     [super viewDidLoad];
     SEL myMethod = @selector(myLog:parm:parm:);
     SEL myMethod2 = @selector(myLog);
     NSMethodSignature * sig  = [[self  class ] instanceMethodSignatureForSelector:myMethod];
     NSInvocation * invocatin = [NSInvocation invocationWithMethodSignature:sig];
     ViewController * view = self;
     [invocatin setArgument:&view atIndex:0];
     [invocatin setArgument:&myMethod2 atIndex:1];
     int  a=1;
     int  b=2;
     int  c=3;
     [invocatin setArgument:&a atIndex:2];
     [invocatin setArgument:&b atIndex:3];
     [invocatin setArgument:&c atIndex:4];
     [invocatin retainArguments];
     [invocatin invoke];
}
-( void )myLog:( int )a parm:( int )b parm:( int )c{
     NSLog(@ "MyLog%d:%d:%d" ,a,b,c);
}

2、这里的传参方式必须是传递参数地址。

二、NSInvocation的返回值

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
- ( void )viewDidLoad {
     [super viewDidLoad];
     SEL myMethod = @selector(myLog:parm:parm:);
     NSMethodSignature * sig  = [[self  class ] instanceMethodSignatureForSelector:myMethod];
     NSInvocation * invocatin = [NSInvocation invocationWithMethodSignature:sig];
     [invocatin setTarget:self];
     [invocatin setSelector:myMethod2];
     ViewController * view = self; 
     int  a=1;
     int  b=2;
     int  c=3;
     [invocatin setArgument:&view atIndex:0];
     [invocatin setArgument:&myMethod2 atIndex:1];
     [invocatin setArgument:&a atIndex:2];
     [invocatin setArgument:&b atIndex:3];
     [invocatin setArgument:&c atIndex:4];
     [invocatin retainArguments];
     //我们将c的值设置为返回值
     [invocatin setReturnValue:&c];
     int  d;
     //取这个返回值
     [invocatin getReturnValue:&d];
     NSLog(@ "%d" ,d);
     
}
-( int )myLog:( int )a parm:( int )b parm:( int )c{
     NSLog(@ "MyLog%d:%d:%d" ,a,b,c);
     return  a+b+c;
}

注意:这里的操作传递的都是地址。如果是OC对象,也是取地址。

三、关于内存

可以注意到- (void)retainArguments;这个方法,它会将传入的所有参数以及target都retain一遍。

目录
相关文章
|
Swift iOS开发 Perl
如何解决Swift混编的module编译错误
前言很多iOS工程都是基于Object-C开发,再逐步向Swift演进,演进过程中不可避免要进行Swift混编。Swift模块需要支持LLVM Module规范,混编工程会遇到各种Module编译错误。这对于不熟悉的同学来说简直是灾难,严重影响开发效率。本文会介绍常见的Module编译错误,希望对大家有所帮助。常见错误1:Could not build module xxx当一个OC模块引用了Sw
7614 1
如何解决Swift混编的module编译错误
|
存储 安全 编译器
【C++ 17 新功能 std::visit 】深入解析 C++17 中的 std::visit:从原理到实践
【C++ 17 新功能 std::visit 】深入解析 C++17 中的 std::visit:从原理到实践
1182 1
|
8月前
|
前端开发 JavaScript 测试技术
大厂开源项目,真的太爽啦,字节跳动出品!这个设计系统开源神器,让你的产品颜值与效率齐飞
Semi Design是由抖音前端团队开源的企业级设计系统,**同时支持React/Vue双框架**,提供超过60+高质量组件。不同于普通UI库,它从**设计工程化**角度出发,打通Figma设计资源与前端代码的桥梁,真正实现设计稿到代码的"零误差还原"。目前已在字节跳动200+项目中验证,日均调用量超过3000万次。
213 7
|
8月前
|
人工智能 API 语音技术
WhisperChain:开源 AI 实时语音转文字工具!自动消噪优化文本,效率翻倍
WhisperChain 是一款基于 Whisper.cpp 和 LangChain 的开源语音识别工具,能够实时将语音转换为文本,并自动清理和优化文本内容,适用于会议记录、写作辅助等多种场景。
2151 2
WhisperChain:开源 AI 实时语音转文字工具!自动消噪优化文本,效率翻倍
|
12月前
|
API 语音技术
基于Asterisk和TTS/ASR语音识别的配置示例
基于Asterisk和TTS/ASR语音识别的配置示例如下:1. 安装Asterisk:首先,确保你已在服务器上成功安装Asterisk。可以选择从Asterisk官方网站下载最新版本的安装包并按照指南进行安装。2. 安装TTS引擎:选择适合你需求的TTS(Text-to-Speech)引擎,如Google Text-to-Speech、Microsoft Azure Cognitive Services等。按照所选TTS引擎的文档和指示进行安装和配置。3. 配置Asterisk:编辑Asterisk的配置文件,通常是`/etc/asterisk/extensions.conf
213 5
|
NoSQL 关系型数据库 MySQL
[AIGC] 分布式锁及其实现方式详解与Python代码示例
[AIGC] 分布式锁及其实现方式详解与Python代码示例
412 1
|
关系型数据库 PostgreSQL
【一文搞懂PGSQL】5. 流复制
PostgreSQL流复制架构支持多种常见配置,包括基本的主从复制、结合PGPool-II的读写分离以及使用repmgr实现高可用性。基础环境中,主节点与备用节点分别位于不同IP。配置涵盖创建复制用户、调整核心参数以支持流复制,并确保归档与日志功能正常工作。从节点需通过备份恢复并配置为待机模式,以实现数据同步。此外,还介绍了如何验证复制状态及手动切换主从节点的方法,以及同步复制参数的配置细节。
|
机器学习/深度学习 存储 监控
基于YOLOv8深度学习的高压输电线绝缘子缺陷智能检测系统【python源码+Pyqt5界面+数据集+训练代码】深度学习实战、目标检测
基于YOLOv8深度学习的高压输电线绝缘子缺陷智能检测系统【python源码+Pyqt5界面+数据集+训练代码】深度学习实战、目标检测
|
供应链 Shell Python
经济订货量(Economic Order Quantity,简称EOQ)
经济订货量(Economic Order Quantity,简称EOQ)
|
程序员 索引
SwiftUI极简教程18:SwipeCard卡片滑动效果的使用(上)
SwiftUI极简教程18:SwipeCard卡片滑动效果的使用(上)
1396 0
SwiftUI极简教程18:SwipeCard卡片滑动效果的使用(上)
下一篇
开通oss服务