Object-C中编写省略参数的多参函数

Object-C中编写省略参数的多参数函数

引语:

在Object-C中,我们会遇到很多像NSLog这样的函数,其中参数的个数不确定,由程序员自由控制,在初始化数组,字典等方面应用广泛,那么,这类的函数是如何实现的呢?我们怎么编写我们自己的省略参数的函数呢?当然,这不是唯一的多参函数的处理方法,你也可以通过一个字典或者数组传递参数。但C为我们提供的这样的一种机制,无疑是最方便的。

一、了解几个概念

va_list

C语言中定义的一个指针,用于指向当前的参数。

va_start(ap,param)

这个宏是初始化参数列表,其中第一个参数是va_list对象,第二个参数是参数列表的第一个参数。

va_arg(ap, type)

一个用于取出参数的宏,这个宏的第一个参数是va_list对象,第二个参数是要取出的参数类型。

va_end(ap)

这个宏用于关闭取参列表

二、多参函数的取参原理

在编写我们自己的多参函数之前,明白函数的取参原理是十分重要的,首先,函数的参数是被放入我们内存的栈段的,而且放入的顺序是从后往前放入,比如如果一个函数参数如下:

void func(int a,int b,int c,int d)

那么传递参数的时候参数d先入栈,接着是c、b、a。如此这样,在取参的时候,根据堆栈的取值原则,则取值顺序为a、b、c、d。所以在原理上,只要我们知道第一个参数的地址和每个参数的类型,我们就可以将参数都取出来。而上面介绍的几个宏,就是帮助我们做这些的。

三、声明与实现省略参数的多参函数

"..."这个符号就是我们用来实现省略参数函数的符号。例如我们模拟实现一个log函数如下:

-(void)myLog:(NSString *)str,...{//省略参数的写法
    va_list list;//创建一个列表指针对象
    va_start(list, str);//进行列表的初始化,str为省略前的第一个参数,及...之前的那个参数
    NSString * temStr = str;
    while (temStr!=nil) {//如果不是nil,则继续取值
         NSLog(@"%@",temStr);
         temStr = va_arg(list, NSString*);//返回取到的值,并且让指针指向下一个参数的地址
    }
    va_end(list);//关闭列表指针
}

注意,调用时,我们必须在参数的最后加上nil这个判断结束的条件:

[self myLog:@"312",@"321", nil];//必须有nil

四、一点补充

细心的你可能发现了,这里的nil是我们在调用函数时手动加上的,可是系统的许多函数在我们调用时,系统直接帮我们加上了参数结尾的那个nil,例如

NSArray * array = [NSArray arrayWithObjects:(id), nil]

这是如何做到的呢?我们只需要在函数的声明里加上一个宏,就可以实现这个功能,修改如下:

-(void)myLog:(NSString *)str,...NS_REQUIRES_NIL_TERMINATION{//这里加上一个宏
    va_list list;
    va_start(list, str);
    NSString * temStr = str;
    while (temStr!=nil) {
         NSLog(@"%@",temStr);
         temStr = va_arg(list, NSString*);
    }
    va_end(list);
}

顾名思义,这个宏的作用就是在结束位置加上我们需要的nil。

时间: 2024-11-14 12:23:40

Object-C中编写省略参数的多参函数的相关文章

python中编写带参数decorator

考察上一节的 @log 装饰器: def log(f): def fn(x): print 'call ' + f.__name__ + '()...' return f(x) return fn 发现对于被装饰的函数,log打印的语句是不能变的(除了函数名). 如果有的函数非常重要,希望打印出'[INFO] call xxx()...',有的函数不太重要,希望打印出'[DEBUG] call xxx()...',这时,log函数本身就需要传入'INFO'或'DEBUG'这样的参数,类似这样:

在C/Objc中 写一个参数个数可变的函数示例

////  main.c//  UncertainParametersAndParameterSentinel////  Created by llc on 15/2/12.//  Copyright (c) 2015年 llc. All rights reserved.// #include <stdio.h>#include <stdarg.h> int addemUp(int firstNum,...);void printStrings(char * first,...)

JQuery中Ajax详细参数使用案例

JQuery中Ajax详细参数使用案例 参考文档:http://www.jb51.net/shouce/jquery1.82/ 参考文档:http://jquery.cuishifeng.cn/jQuery.Ajax.html 注意事项 本案例演示测试了官方文档中不常用的参数使用方法 前端代码 function theFileUploadGai() {//执行上传 var zhi={ "a1":["你好啊","こんにちは","hello

solr查询参数、语法、函数总结

查询参数: qt               query type,指定那个类型来处理查询请求,一般不用指定,默认是standard. fl      查询返回哪些字段. q       query(查询字符串,必须的). fq               过滤查询   (filter query). start           分页用,起始位置. rows           分页用,返回多少条记录. sort             排序. wt               输出格式  

编写高质量代码改善C#程序的157个建议——建议43:让接口中的泛型参数支持协变

建议43:让接口中的泛型参数支持协变 除了上一建议中提到的使用泛型参数兼容接口不可变性外,还有一种办法是为接口中的泛型声明加上out关键字来支持协变,如下所示: interface ISalary<out T> //使用out关键字 { void Pay(); } static void Main(string[] args) { ISalary<Programmer> s = new BaseSalaryCounter<Programmer>(); ISalary&l

sqlplus中常用设置参数

一.各种设置参数解释 转自http://baike.baidu.com/view/1239908.htm Sql*plus是一个最常用的工具,具有很强的功能,主要有: 1. 数据库的维护,如启动,关闭等,这一般在服务器上操作. 2. 执行sql语句执行pl/sql. 3. 执行sql脚本. 4. 数据的导出,报表. 5. 应用程序开发.测试sql/plsql. 6. 生成新的sql脚本. 7. 供应用程序调用,如安装程序中进行脚本的安装. 8. 用户管理及权限维护等. 二.下面就介绍一下一些常用

如何在Ruby中编写微服务?

[编者按]本文作者为 Pierpaolo Frasa,文章通过详细的案例,介绍了在Ruby中编写微服务时所需注意的方方面面.系国内 ITOM 管理平台 OneAPM 编译呈现. 最近,大家都认为应当采用微服务架构.但是,又有多少相关教程呢?我们来看看这篇关于用Ruby编写微服务的文章吧. 人人都在讨论微服务,但我至今也没见过几篇有关用Ruby编写微服务的.像样的教程.这可能是因为许多Ruby开发人员仍然最喜欢Rails架构(这没什么不好,Rails本身也没什么不好,但是Ruby可以做到的事还有很

Java中编写一个完美的equals方法

首先看下Java语言规范对equals方法的要求: 1,自反性,对于任何非控引用x,x.equals(x)都应该返回true. 2,对称性,对于任何引用x和y,如果x.equals(y)返回true,那么y.equals(x)也应该返回true. 3,传递性,如果x.equals(y),y.equals(z)都返回true,那么,x.equals(z)返回true. 4,一致性,如果x和y引用的对象没有发生变化,那么无论调用多少次x.equals(y)都返回相同的结果. 5,对于任意非空引用x,

在ubuntu linux 中编写一个自己的python脚本

在ubuntu linux 中编写一个自己的简单的bash脚本. 实现功能:终端中输入简单的命令(以pmpy为例(play music python),为了区别之前说的bash脚本添加了py后缀),来实现音乐的播放.注:本人ununut中安装了audacious,所以就以audacious为例,来实现音乐的播放. 第一步:进入一个目录,最好是自己特别选定的,如果用文件浏览器可以新建一个名为pmpy空白文档文件:如果是用终端可以输入命令:vi pmpy(可能会因为位置问题,需要添加sudo) 第二