《企业级ios应用开发实战》一3.6 可变参数

简介: 本节书摘来自华章出版社《企业级ios应用开发实战》一 书中的第3章,第3.6节,作者:杨宏焱,更多章节内容可以访问云栖社区“华章计算机”公众号查看。

3.6 可变参数

我们知道,C和C++语言支持可变参数的函数,例如我们常用的NSLog和printf函数。Objective-C作为C语言的超集,当然毫无例外地也支持可变参数。迄今为止,我们至少用过了一种使用可变参数的方法,即NSString的stringWithFormat:方法。
C语言通过stdarg.h库支持可变参数,Objective-C 也不例外。在C语言中,如果你要使用可变参数,必须包含头文件stdarg.h,但在Cocoa中却不必,因为苹果已经在 NSObjC Runtime.h中包含了stdarg.h。
stdarg.h的定义如下:

typedef __void  va_list;
#define va_start(ap, param) __builtin_va_start(ap, param)
#define va_end(ap)            __builtin_va_end(ap)
#define va_arg(ap, type)     __builtin_va_arg(ap, type)

首先定义了一个va_list类型,其实就是一个void,即可以指向任何类型的指针。你可以把它看成是char,因为char*实际上也可以指向任何内存单元的地址。
然后是3个预定义宏,va_start、va_end和va_arg。可以看出stdarg.h完全是以“预定义宏”这种“古老”的方式来支持可变参数的。
接下来我们看一个例子,该方法使用了一个可变参数,并将这些可变参数进行了累加,然后返回一个NSNumber:

- (NSNumber ) addValues:(int) count, ... {
     va_list args;
     va_start(args, count);
     NSNumber value;
     double retval;
     for( int i = 0; i < count; i++ ) {
          value = va_arg(args, NSNumber );
          retval += [value doubleValue];
     }
     va_end(args);
     return [NSNumber numberWithDouble:retval]; 
}

代码说明:
第1行是方法定义,该定义应当加到头文件中。省略号...表明方法接收一系列数目不定的参数,在...前面至少需要指定一个任意类型的参数。有时我们必须知道参数的个数以防止出现无效的引用,但在某种情况下,参数个数是可以通过其他参数推断出来的(例如NSLog或printf函数可以通过计算%号的个数推断可变参数的个数),或者对于NSMutableArray来说,它总是以nil终止。
如果是最后一种方法,我们可以把方法重新定义为:

- (NSNumber ) addValues:(NSNumber ) firstNumber, ... 

这样,我们就可以用以下调用方式代替“addValues:3,num1,num2,num3”:

addValues:num1,num2,num3;

这样,我们就可以省略第1个表示可变参数个数的int参数。
第2行中的va_list是void *类型,因此它实际上是一个可变的对象数组。
第3行用args来存放可变参数列表,而count则表示函数最后一个参数(即第一个“固定参数”)。这将使编译器把args指向第1个参数后的位置(通过count地址加上count变量的长度)。
很奇怪吗?可变参数中第1个参数的位址为什么是“count地址+count的长度”?因为对于大多数C编译器,函数栈中参数的存放顺序是从右到左的,也就是说先放入可变参数的最后一个参数,再放可变参数的倒数第2个参数……,然后放可变参数的第1个参数,最后是固定参数count。而与此同时,栈的方向是向下的,即先入栈的数据位于高地址,后入栈的数据则位于栈的起始地址。这样,实际上最后放入的固定参数count的地址变成了栈的起始地址。而紧随count之后的地址则是可变参数的第1个参数地址,即“count地址+count的长度”,因此编译器要能找到第1个可变参数的地址,只要知道1个参数:count就够了,由count取得函数栈的起始地址,加上sizeof(count),得到第1个可变参数的地址。va_start的第1个参数args是一个输出参数,经过va_start调用之后,args将等于arg_start计算出来的第1个可变参数的地址。
第6行是一个for循环,因为我们无法通过 args 自身推断 args 的大小,因此必须显式地用count来指定args的大小。或者可以使用nil终止的列表来检索可变参数。
如果你使用nil终止的数组作为可变参数,则应该用下面一行来代替第6~7行:

while( value = va_arg( args, NSNumber  ) )

第 7 行将 args 中的下一个参数放入 value,并显式地转为 NSNumber*(如果不知道类型,可以用id)。
第10行表明,一旦使用完args列表,就关闭它 。
提示:如果你使用va_arg(args,double)(或者float等其他原始类型),那么当你试图传递一系列整数作为参数时(例如:addValues:4,4,3,2,1),可能会出现一些古怪的结果。而如果你显式地将这些参数说明为double(例如,double num1,double num2,double num3,double num4)则不会有什么问题。
这是因为,如果编译器看到一个方法有一个double参数但你却传递了一个整数给这个方法时,它会进行类型转换。但如果方法使用了可变参数,编译器无法知道参数所使用的类型,因此编译器只会简单地把参数作为整型处理。

相关文章
|
1月前
|
开发框架 前端开发 Android开发
Flutter 与原生模块(Android 和 iOS)之间的通信机制,包括方法调用、事件传递等,分析了通信的必要性、主要方式、数据传递、性能优化及错误处理,并通过实际案例展示了其应用效果,展望了未来的发展趋势
本文深入探讨了 Flutter 与原生模块(Android 和 iOS)之间的通信机制,包括方法调用、事件传递等,分析了通信的必要性、主要方式、数据传递、性能优化及错误处理,并通过实际案例展示了其应用效果,展望了未来的发展趋势。这对于实现高效的跨平台移动应用开发具有重要指导意义。
160 4
|
2月前
|
设计模式 安全 Swift
探索iOS开发:打造你的第一个天气应用
【9月更文挑战第36天】在这篇文章中,我们将一起踏上iOS开发的旅程,从零开始构建一个简单的天气应用。文章将通过通俗易懂的语言,引导你理解iOS开发的基本概念,掌握Swift语言的核心语法,并逐步实现一个具有实际功能的天气应用。我们将遵循“学中做,做中学”的原则,让理论知识和实践操作紧密结合,确保学习过程既高效又有趣。无论你是编程新手还是希望拓展技能的开发者,这篇文章都将为你打开一扇通往iOS开发世界的大门。
|
2月前
|
搜索推荐 IDE API
打造个性化天气应用:iOS开发之旅
【9月更文挑战第35天】在这篇文章中,我们将一起踏上iOS开发的旅程,通过创建一个个性化的天气应用来探索Swift编程语言的魅力和iOS平台的强大功能。无论你是编程新手还是希望扩展你的技能集,这个项目都将为你提供实战经验,帮助你理解从构思到实现一个应用的全过程。让我们开始吧,构建你自己的天气应用,探索更多可能!
77 1
|
1月前
|
安全 Swift iOS开发
Swift 与 UIKit 在 iOS 应用界面开发中的关键技术和实践方法
本文深入探讨了 Swift 与 UIKit 在 iOS 应用界面开发中的关键技术和实践方法。Swift 以其简洁、高效和类型安全的特点,结合 UIKit 丰富的组件和功能,为开发者提供了强大的工具。文章从 Swift 的语法优势、类型安全、编程模型以及与 UIKit 的集成,到 UIKit 的主要组件和功能,再到构建界面的实践技巧和实际案例分析,全面介绍了如何利用这些技术创建高质量的用户界面。
33 2
|
1月前
|
JSON 前端开发 API
探索iOS开发之旅:打造你的第一个天气应用
【10月更文挑战第36天】在这篇文章中,我们将踏上一段激动人心的旅程,一起构建属于我们自己的iOS天气应用。通过这个实战项目,你将学习到如何从零开始搭建一个iOS应用,掌握基本的用户界面设计、网络请求处理以及数据解析等核心技能。无论你是编程新手还是希望扩展你的iOS开发技能,这个项目都将为你提供宝贵的实践经验。准备好了吗?让我们开始吧!
|
1月前
|
Swift iOS开发 UED
如何使用Swift和UIKit在iOS应用中实现自定义按钮动画
本文通过一个具体案例,介绍如何使用Swift和UIKit在iOS应用中实现自定义按钮动画。当用户点击按钮时,按钮将从圆形变为椭圆形,颜色从蓝色渐变到绿色;释放按钮时,动画以相反方式恢复。通过UIView的动画方法和弹簧动画效果,实现平滑自然的过渡。
59 1
|
2月前
|
Swift iOS开发 UED
如何使用Swift和UIKit在iOS应用中实现自定义按钮动画
【10月更文挑战第18天】本文通过一个具体案例,介绍如何使用Swift和UIKit在iOS应用中实现自定义按钮动画。当用户按下按钮时,按钮将从圆形变为椭圆形并从蓝色渐变为绿色;释放按钮时,动画恢复原状。通过UIView的动画方法和弹簧动画效果,实现平滑自然的动画过渡。
62 5
|
3月前
|
存储 IDE 开发工具
移动应用开发之旅:打造你的首个iOS应用
【9月更文挑战第23天】在数字化浪潮中,移动应用已成为连接用户与数字世界的关键桥梁。本文将带领读者踏上开发属于自己的第一个iOS移动应用的旅程,从理解移动操作系统的核心概念出发,逐步深入到实际的应用构建过程中。通过简洁明了的语言和具体的代码示例,我们将一起探索如何在苹果的iOS平台上实现一个简单的“待办事项列表”应用,让读者不仅能够学习到编程知识,还能体会到将想法转化为现实产品的成就感。无论你是编程新手还是希望扩展技能的开发者,这篇文章都将为你提供一个实用的指南,帮助你迈出成为移动应用开发者的第一步。
|
3月前
|
开发框架 Android开发 iOS开发
探索安卓与iOS开发的差异:构建未来应用的指南
在移动应用开发的广阔天地中,安卓与iOS两大平台各占半壁江山。本文将深入浅出地对比这两大操作系统的开发环境、工具和用户体验设计,揭示它们在编程语言、开发工具以及市场定位上的根本差异。我们将从开发者的视角出发,逐步剖析如何根据项目需求和目标受众选择适合的平台,同时探讨跨平台开发框架的利与弊,为那些立志于打造下一个热门应用的开发者提供一份实用的指南。
71 5
|
3月前
|
开发工具 Android开发 iOS开发
探索安卓与iOS开发的差异:构建未来应用的关键考量
在数字时代的浪潮中,安卓和iOS这两大操作系统如同双子星座般耀眼夺目,引领着移动应用的潮流。它们各自拥有独特的魅力和深厚的用户基础,为开发者提供了广阔的舞台。然而,正如每枚硬币都有两面,安卓与iOS在开发过程中也展现出了截然不同的特性。本文将深入剖析这两者在开发环境、编程语言、用户体验设计等方面的显著差异,并探讨如何根据目标受众和项目需求做出明智的选择。无论你是初涉移动应用开发的新手,还是寻求拓展技能边界的资深开发者,这篇文章都将为你提供宝贵的见解和实用的建议,帮助你在安卓与iOS的开发之路上更加从容自信地前行。