Variable argument lists in Cocoa

Spark读取hudi中的数据报Unable to infer schema for Parquet. It must be specified manually.; Exception in thread "main" org.apache.spark.sql.AnalysisException: Unable to infer schema for Parquet. It must be specified manually.; 阅读详情

Variable argument lists in Cocoa

This week I'll talk about methods that take variable numbers of arguments, also known as variadic methods. I'll show you the Objective-C syntax and implementation, give a quick rundown of the ways that Cocoa classes provide variable argument support and I'll also show you a way to fake va_list parameters to handle Cocoa's variadic method equivalents at runtime.

Variable arguments basics

Passing a variable number of arguments to a method is a convenient way to handle a list of variables that are in scope at compile time.

The Objective-C language handles variable arguments in the same way that Standard C does. Normally, you will encounter variable argument lists in one of two forms: "Format strings" or "Nil terminated lists".

Format strings

The traditional example for variable arguments are format strings. Format strings contain a number of "placeholders" (escape sequences starting with a "%" sign) that are replaced with data from variables when the format string and the variable arguments are passed to the method.

NSString *myString = [ NSString stringWithFormat :
     @"Number %d, String: %@, Float: %g" , 1 2 3 , @"SomeString" , 3 4 .5 ];

This method is declared as follows:

+ ( id )stringWithFormat:( NSString *)format, ...;

The "..." is the variable argument.

This method declares a first parameter but everything else is "variable". This does not mean optional. Instead, it means that a number of extra parameters will be required. Exactly what number and their types depends on how the method works.

The documented behavior for stringWithFormat:, is that it scans the format string and requires 1 variable argument for every escape sequence (in this case there are 3) and the type must match the specifier for each escape sequence (in this case, "d", "@" and "g" specify int, id and float/double).

Nil terminated lists

The other common type of variable argument list method is one that takes a list of objects terminated by nil.

In Cocoa, these are commonly used for constructing collection objects.

NSArray *myArray = [ NSArray arrayWithObjects : @"One" , @"Two" , @"Three" , nil ];

The documented rule for this type of method is that the last argument must be nil.

Getting argument numbers wrong

The details of "Format strings" and "Nil terminated lists" reveal the difficulty with variable argument lists in Objective-C: knowing how many arguments exist — i.e. when to stop reading arguments.

Objective-C, like C, does not have a runtime argument count, nor does it pass runtime argument types. So the number and types of arguments must be determined by some other policy. In the case of these two examples, the argument count is, respectively: the number of escape sequences in the "Format string" or the number of arguments before the first nil. For "format strings" the types are determined by the escape sequences, for "Nil terminated" lists, the arguments are always pointers.

The penalty for getting variable argument list wrong can be severe: the compiler won't typically notice a problem and your code will crash or behave very strangely at runtime. The important lesson to learn is that you should only use variable arguments in a situation where the policy for numbers and types of arguments is very clear.

You can improve the compiler verification of variable argument lists by using the -Wformat compiler flag in GCC. See below for more about how this works.
Implementing variable arguments for your own methods

Lets look at the implementation for the example class:

@interface StringContainer : NSObject
{
     NSString *contents;
}
@end

Imagine we wanted a method that would set the contents string this class contains by concatenating a variable argument list of strings together.

In this case, we will use the nil terminated approach — the list of strings to concatenate will end with a nil, so we know when to stop reading.

The declaration for the method looks like this:

- ( void )setContentByAppendingStrings:( NSString *)firstString, ...
     NS_REQUIRES_NIL_TERMINATION ;

The NS_REQUIRES_NIL_TERMINATION part is a macro that tells the compiler that invocations of this method must include a nil-terminated list of arguments. Failure to nil-terminate the list will result in a compiler warning if -Wformat is enabled.

Annoyingly, -Wformat is not enabled by default. Double annoyingly, it is named "Typecheck Calls to printf/scanf" in the "GCC 4.0 Warnings" section of the "Build Settings" in Xcode, even though this setting affects the NS_REQUIRES_NIL_TERMINATION macro which is used commonly throughout all of Cocoa — far beyond printf and scanf.

The implementation of this method is as follows:

- ( void )setContentByAppendingStrings:( NSString *)firstArg, ...
{
     NSMutableString *newContentString = [ NSMutableString string ];
     va_list args;
     va_start(args, firstArg);
     for ( NSString *arg = firstArg; arg != nil ; arg = va_arg(args, NSString*))
     {
         [newContentString appendString :arg];
     }
     va_end(args);
     
     [contents autorelease ];
     contents = [newContentString retain ];
}

The va_list, va_start, va_arg and va_end are all standard C syntax for handling variable arguments. To describe them simply:

  • va_list - A pointer to a list of variable arguments.
  • va_start - Initializes a va_list to point to the first argument after the argument specified.
  • va_arg - Fetches the next argument out of the list. You must specify the type of the argument (so that va_arg knows how many bytes to extract).
  • va_end - Releases any memory held by the va_list data structure.

Generally speaking, you can use this for loop for any variable argument situation where your arguments are all the same type. Other cases are a bit trickier but far less common — I'm sure you can work out how they would work if needed.

va_list in Cocoa

A number of classes in Cocoa have methods that take variable numbers of arguments. In most cases, these classes will also have an equivalent method that takes a va_list.

We can see an example of these va_list equivalents by looking at NSString. NSString declares the class method stringWithFormat:... (which takes a variable number of arguments) and NSString also declares the instance method initWithFormat:arguments: (where the arguments parameter is a va_list) which handles the equivalent behavior of stringWithFormat:....

These va_list methods are used in the situation where your class defines a method with a variable argument list and you need to pass those variable arguments into the Cocoa method. For example, if the StringContainer class listed above declared the method:

- ( void )setContentsWithFormat:( NSString *)formatString, ...;

The implementation of this method would be as follows:

- ( void )setContentsWithFormat:( NSString *)formatString, ...
{
     [contents autorelease ];
 
     va_list args;
     va_start(args, formatString);
     contents = [[ NSString alloc ] initWithFormat :formatString arguments :args];
     va_end(args);
}

The va_list parameter allows us to pass our own variable argument list to the Cocoa method so that the Cocoa method can handle the arguments.

Creating a fake va_list

The va_list methods in Cocoa are helpful if you actually have a variable argument list.

There is another situation you many encounter though: you want to use a method like -[NSString initWithFormat:arguments:] using a runtime generated array of arguments. There is no NSString format method that takes an NSArray of arguments, so how could we handle a format string at runtime?

The answer lies in how va_list works. While GCC makes it very clear that va_list is "platform specific", the reality is that on Mac and iPhone Objective-C platforms, it is simply a byte buffer containing the arguments. In fact, if you've ever used an ABI inspection tool (like class-dump-x) on a method taking a variable argument list, you'll see that it is simply a char *.

To show how we can use this knowledge, consider the following method on the StringContainer class:

- ( void )setContentsWithFormat:( NSString *)formatString arguments :( NSArray *)arguments;

If we assume that all of the variable arguments required fro the formatString are objects, then we can implement this method as follows:

- ( void )setContentsWithFormat:( NSString *)formatString arguments :( NSArray *)arguments
{
     [contents autorelease ];
 
     char *argList = ( char *)malloc(sizeof( NSString *) * [arguments count ]);
     [arguments getObjects :( id *)argList];
     
     contents = [[ NSString alloc ] initWithFormat :formatString arguments :argList];
     
     free(argList);
}

What I've done here is simply copied the NSArray to a C-style byte buffer and then passed that buffer to the initWithFormat:arguments: method.

If your arguments are not all Objective-C objects, you'd need to take greater care to assemble the byte buffer but the principles would be the same.

Some variadic notes
NSInvcation incompatibility

NSInvocation does not support variadic methods. I'm not sure why this is — maybe NSInvocation wants to avoid making assumptions about va_list, maybe it is because NSMethodSignature can't describe the storage requirements of variable arguments for NSInvocation or maybe the Cocoa developers have families and just wanted to go home early.

Variadic macros

You can also write variadic macros, much like variadic methods, by using the ##__VA_ARGS__ placeholder in your macros. See the macros near the bottom of my post titled Supersequent Implementation for an example of how this works. As discussed briefly in that article, the version of GCC used for iPhone development handles variadic macros slightly differently, so pay attention to the differences when targetting the iPhone platform.

Conclusion

Variadic methods require a degree more care than regular methods because variable argument lists have no "introspection" (you cannot ask for the number and type of arguments) — so their usage relies on documentation and implicit agreements between the sender and receiver.

In your own code, variadic methods should be used sparingly — passing variables in an NSArray or NSDictionary is safer (if slightly slower and syntactically more verbose) due to the fact that these classes do offer introspection.

When the implicit sender/receiver agreement is clear, variadic methods work well. They certainly make creating instances of NSArray, NSSet, NSDictionary easier and they are the only way to create a formatted NSString in a single invocation.

Variable-length Argument Lists 立即下载

相关推荐

产业园区如何高效对接企业技术需求与高校科研成果?.docx

科易网基于AI模型+行业大数据,在技术转移、成果转化、技术经纪、知识产权、产业创新、科技招商等垂直领域的多样化应用场景,推出科技创新领域的AI+工具与数智化解决方案,推动科技创新与产业创新智能化发展。

解决SparkSql 读取parquet或者Orc文件报错Unable to infer schema for Parquet. It must be specified manually

val conf = new SparkConf().setAppName(getClass.getSimpleName).setMaster("local[4]") val spark = SparkSession.builder().config(conf).getOrCreate() val sc = spark.sqlContext val path="/data_file/tp=acc...

qq_40625030的博客 2万+

国央企创新负责人如何快速掌握本地科技资源分布与匹配情况?.docx

科易网基于AI模型+行业大数据,在技术转移、成果转化、技术经纪、知识产权、产业创新、科技招商等垂直领域的多样化应用场景,推出科技创新领域的AI+工具与数智化解决方案,推动科技创新与产业创新智能化发展。

variable argument list

variable argument:#define _ADDRESSOF(v)   ( &reinterpret_cast(v) )#elif   defined(_M_IX86)//Interge promotions#define _INTSIZEOF(n)   ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )//v

feixie1225的专栏 1075

Unity 数字孪生笔记1 工具介绍

前言 工欲善其事必先利其器 流程思考 数字孪生本质上一种基于实际物理数据的可视化方案,通过对接实际数据,在三维界面中展示孪生结果。 那么这个流程就很简单了。 整个流程分为三块:数据来源-》数据分析-》数据展示 输入数据 首先我们需要考虑我们能拿到什么。第一,实际物体的三维数据从哪里来,第二,实际数据怎么获取到。 模型数据 工业上常常会用到CAD模型,CAD模型具有精确、统一的特点,但是CAD不是三角面片,我们不能直接导出到Unity中,而且导入之后有很多不合理的地方,例如面片杂乱、面数过高等问题。针对这个

Thissky的专栏 1万+

mongoDB 数据莫名其妙的没了

问题 之前没有接触过mongoDB,然后接手一个项目使用的是MongoDB,我就跟着网上的教程搭建一个MongoDB的服务,并运行起来。 一开始没有啥问题,成功的把服务与MongoDB都启动以来,运行也没有问题。但是过了几天,我再次打开时发现,以前的数据神奇的消失了。我还在想,是有什么定时清除的么,还是什么原因导致崩了然后数据就被清理了。 查找原因 第一次遇到我没有在意,重启了一下MongoDB,...

技术熊的博客小窝 1万+

parquet列式文件实战(未完,待续)

parquet列式文件实战 parquet code demo http://www.programcreek.com/java-api-examples/index.php?source_dir=hiped2-master/src/main/java/hip/ch3/parquet/ExampleParquetMapReduce.java 开源列式存储引擎Parqu...

weixin_30859423的博客 148

国产NAS哪家强?极空间&绿联&威联通&群晖对比

随着个人数据量暴增,传统网盘空间不足和下载速度慢问题愈发凸显。新势力NAS,作为数据管理领域的璀璨新星,正以其独特的互联网设计理念,引领家庭及小型办公用户向数据存储的新纪元。它不仅仅是一款产品,更是为初学者用户量身定制的贴心伙伴。其系统界面之简洁、操作流程之直观,让人一目了然,真正实现了“即插即用”的极致便捷。同时,新势力NAS在保持专业水准的同时,更提供了多样化的扩展性配置,轻松应对各种数据存储与管理需求,让家庭及小型办公场景下的数据管理变得前所未有的简单与高效。

专注大数据、云计算、人工智能、机器人领域技术传播 3647

Mybatis报TypeException:JDBC requires that the JdbcType must be specified for all nullable parameters.

查看打印出来的sql是否有错。 如何没有错误就去查看对应的XXXDao.xml文件,查看文件内的语句(注意的是判断的部分) 这好像是sql语句有错才报出

千尘梦博客 1万+

VS Code配置MinGW64编译非线性优化库NLopt

VS Code用MinGW64编译C++代码安装MSYS2软件并配置非线性优化库NLopt和测试引用库代码的完整具体步骤。:若使用非UCRT版本(如传统MinGW64),替换所有路径中的。库文件会自动安装到MSYS2的。

weixin_30777913的博客 902

Kettle实战:如何用JavaScript脚本实现数据库表批量操作(附完整代码)

本文详细介绍了如何利用Kettle的JavaScript脚本实现数据库表批量操作,包括表名采集、循环遍历和动态SQL生成等关键步骤。通过实战案例和完整代码示例,帮助ETL工程师高效处理数据密集型任务,提升数据库操作的自动化水平。

weixin_29019241的博客 232

Mybaties-foreach 遇到mybatis 异常: JDBC requires that the JdbcType must be specified for all nullable pa

项目上遇到了mybatis 异常: JDBC requires that the JdbcType must be specified for all nullable parameters.1.此种情况 由于 mybatis 映射文件中,sql 参数值为 null , 由于 还有另一种 关于 参数为 null 的出现 oracle 无效列类型错误  1111 的异常,那种情况 发现 写的参数 是...

qq_29178991的博客 979

百度api爬虫(1)从百度api中爬取地点数据

几句废话———— 百度api就是基于百度地图的开发接口,这么大的地图公司居然给开发接口真的很感激哎! 所以,言归正传。现在项目需求要把乌鲁木齐市内的所有清真寺都用矢量圈出来,然后做点统计啥的,最后做个专题图产品。 怎么把清真寺找出来? 领导的意思是你一个一个上网搜吧,这就是你的工作量。那我当然要想办法啊,边工作边学习工作才有意义嘛! 然后就发现百度api可以实现我的需求。其实可以通过py...

weixin_43105618的博客 8900

关于eclipse的启动问题

包括英文版本和中文版本两种的说明, 特别需要值得一提的是那个 -nl 参数, 可以指定程序启动时所使用的语言. 例如:eclipse -nl en_US将启动英文语言, 这个特性在安装了国际化语言包以后特别有用, 可以方便的切换各个语言的版本. 注意 IBM WSAD v5.1 也支持这个功能.运行 Eclipse将 Eclipse 驱动程序安装(解压缩)到某个目录(例如,c:/eclip

狗 2438

手把手教你用HiNet实现图像隐藏:可逆神经网络实战指南(附代码)

本文提供了一份基于HiNet框架实现图像隐藏的实战指南。通过详细解析可逆神经网络原理,手把手指导环境搭建、模型构建、损失函数设计及训练流程,并附有完整代码。该方法利用单个可逆网络统一隐藏与恢复过程,结合小波变换提升隐蔽性,为信息隐藏应用提供了高效解决方案。

rgv234567的博客 433

后台管理系统-7-vue3之引入配置文件baseURL

mock=false,本地模拟。mock=true,线上模拟。baseURL+url=实际请求的URL。get请求param参数,post请求data参数。

qq_20466211的博客 208

hive小操作·关于 spark2.4-读取hive3.1事务表

版本信息: spark2.4 hive3.1.1 异常情况: 使用 alter table * compact 'major'后,spark仍无法读取hive事务表中数据 具体流程如下 1、建表语句 create table sugon_transaction(id Int,name String) clustered by (name) into 3 buckets stored ...

concealed0的博客 5430

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

可变长的参数列表(variable-length argument lists)

  可变长参数列表,也叫“varargs”,它允许方法在调用的时候接受任意数目个参数,比如常见的printf()式的打印方式即允许在一个字符串中嵌入多个标签、并且每个标签接收一个参数。   声明“varargs”的方法是:在应该使用的数组方括号的位置,使用省略号(...),例如: void printfObjects ( Object ... list ) {     //list is ...

common_heart的博客 1205

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

报错Attribute "path" is required and must be specified for element type

Attribute “path” is required and must be specified for element type,最近搞一个项目总遇到这个或者类似的问题,其实问题很简单。其实就是batis和MyBatis之间有区别,就是在Batis里面mapping配置文件,你会看到一些元素“XXXClass”或者“Class”。但是在MyBatis里面很多却改成了“XXXType”和“Ty...

FeiChangWuRao的博客 6219

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

函数变参(Variable arguments)

//首先说明问题://      所谓函数变参指的是在编译时无法确定该函数在调用时参数列表中参数的类型和数量,比如大名鼎鼎的“print”系列;  一、99C标中函数变参的实现:      总体上说就是一个类型外加四个宏函数,这都在stdarg.h里有定义。      (1)、类型va_list:使用时首先用va_list定义一个变量用于保存某个变参函数某次变参使用相   

Foru 584

n个数字连乘的计算(Variable-Length Argument List)

程序功能: n个数字的连乘结果=N1*N2*N3*...*Nn,n的大小由用户输入时随意控制,所支持的最大连乘次数则在程序中通过final值设定(暂定为100,够长了吧)。   代码如下: package example; //JHTP Exercise 7.14: Variable-Length Argument List //by pandenghuang@163.com /**(V...

预见未来to50的专栏 3228

variable-length argument lists

The best example of variable-length argument lists function is printf() the proper declaration for printf() is                                                               int printf(char * format,

oliveevilo的专栏 908

android开发时,finish()跟System.exit(0)的区别

finish是Activity的类,仅仅针对Activity,当调用finish()时,只是将活动推向后台,并没有立即释放内存,活动的资源并没有被清理;当调用System.exit(0)时,杀死了整个进程,这时候活动所占的资源也会被释放

影儿的专栏 1893

Warning: Invalid argument supplied for foreach() in 的解决方法

在php中,foreach遍历数组如果不是一个有效数组,php会报一个错误警告 “Warning: Invalid argument supplied for foreach() in ……” ,很多时候这个数组是取自某些数据源,如果数组为空,就会出现错误警告,为了防止这种错误警告,应该在遍历之前对数组变量进行一个判断,如果该变量不是一个有效数组,可以对该变量加个if判断语句,还可以设置该

3万+
上一篇: va_list & va_start & va_arg & va_end
下一篇: [问题]duplicate symbols for architecture armv7
skyming
博客等级 码龄15年 249粉丝 175原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值