objective-c中已有的一些可变参数方法:
- FOUNDATION_EXPORT void NSLog(NSString *format, ...) NS_FORMAT_FUNCTION(1,2);
- - (instancetype)initWithFormat:(NSString *)format, ... NS_FORMAT_FUNCTION(1,2);
- + (instancetype)arrayWithObjects:(id)firstObj, ... NS_REQUIRES_NIL_TERMINATION;
在c/c++中使用可变参数参考《C/C++ 使用可变参数 & 原理》,在objective-c中使用情况也差不多,一些宏定义说明如下:
----------------------------------------------------------------------------------------------------------------------------------
NS_FORMAT_FUNCTION(1,2)的意思:
-
- #if !defined(NS_FORMAT_FUNCTION)
- #if (__GNUC__*10+__GNUC_MINOR__ >= 42) && (TARGET_OS_MAC || TARGET_OS_EMBEDDED)
- #define NS_FORMAT_FUNCTION(F,A) __attribute__((format(__NSString__, F, A)))
- #else
- #define NS_FORMAT_FUNCTION(F,A)
- #endif
- #endif
__attribute__,是GNU编译器的一个特性,这个宏是一个编译器指令,我们在代码中通过定义这个东西,可以inform编译器我们代码 的一些逻辑,从而在编译器避免一些错误,在运行期提高性能。__attribute__在很多代码中都有应用,非常实用。
__attribute__ format ,这个东西能告知编译器,我们的代码在处理printf,scanf这样变参数的函数的时候,哪个参数是format string,哪个参数是参数列表,这样可以避免代码中的一些问题,比如:
-
- extern void eprintf(const charchar *format, ...)
- __attribute__((format(printf, 1, 2)));
-
-
- extern void dprintf(int dlevel, const charchar *format, ...)
- __attribute__((format(printf, 2, 3)));
从上面可以看出,我们定义了eprintf函数,第一个参数是Format String,第二个参数是对应Format String的参数列表,下面的dprintf也是一样,这样一来,编译器就能检测出下面这样的代码错误:
- 1 extern void eprintf(const charchar *format, ...)
- 2 __attribute__((format(printf, 1, 2)));
- 3
- 4 void foo()
- 5 {
- 6 eprintf("s=%s\n", 5);
- 7
- 8 eprintf("n=%d,%d,%d\n", 1, 2);
- 9 }
-
- $ cc -Wall -c test.c
- test.c: In function `foo':
- test.c:6: warning: format argument is not a pointer (arg 2)
- test.c:8: warning: too few arguments for format
其他一些
__attribute__
特性:
__attribute__ const,
这个东西能告诉编译器,
在给定参数的情况下,这个function始终返回同样的值
。这样可以帮助程序提高性能,比如:
- extern int square(int n) __attribute__((const));
- ...
- for (i = 0; i < 100; i++ )
- {
- total += square(5) + i;
- }
如果我们在square函数中没有定义__attribute__ const的话,在下面的那个循环中,程序每次都要产生一个调用square函数的代码。但是这里指定了const之后,程序就知道,对于同一个输入参数 5,返回值都是一样的。
这样程序就会执行一次square,然后cache这个函数的return value
,这样下次循环开始,对square函数的调用就没有函数调用的逻辑了,直接返回上次的结果
----------------------------------------------------------------------------------------------------------------------------------
在objective-c中使用可变参数的例子:
- - (void)foo:(NSString *)format, ...
- {
- va_list args;
- va_start(args, format);
- NSString *str = [[NSString alloc] initWithFormat:format arguments:args];
- va_end(args);
- printf([str UTF8String]);
- [str release];
- }
- - (IBAction) doo: (UIButton*) sender
- {
-
- [self foo : @"My name %@, %@", @"Unmi", @"Yes"];
- }
- - (void)method:(NSString *)value,...
- {
-
- va_list list;
-
- va_start(list, value);
- while (YES)
- {
-
- NSString *string = va_arg(list, NSString*);
- if (!string) {
- break;
- }
- NSLog(@"%@",string);
- }
-
- va_end(list);
- }
函数调用:[self method:@"1",@"2",@"3",nil];像大多数变参函数一样,未尾一定要加上nil,因为这一组宏都没有提供对参数个数的检测,当然你可以会问为何NSLog的参数中我们都不用在末尾添加nil的参数呢,那是因为NSLog的第一个参数是一个格式化字符串,通过这个字条串就能获得后面的参数个数,所以如果你的函数还能有其它的参数能够显式的指出变参个数,当然你也可以书写(但在函数体中需要修改为按已知个数调用va_arg),仍然推荐以上的写法
- @interface sqlHelper : NSObject
- {
- }
- -(int) executeInsertWithSql:(NSString *) statement, ...;
- @end
- .m文件
- -(int) executeInsertWithSql:(NSString *) statement, ...
- {
- PLSqliteDatabase* dbPointer = [SqliteDataBase setUp];
- argsArray = [[NSMutableArray alloc] init];
- id arg;
- va_list argList;
- if(statement)
- {
- va_start(argList,statement);
- while (arg = va_arg(argList,id))
- {
- [argsArray addObject:arg];
- }
- va_end(argList);
- }
- BOOL bResult = [dbPointer executeUpdate:statement,[argsArray objectAtIndex:0],[argsArray objectAtIndex:1]];
- return bResult;
- }
-
- sqlHelper *sqlCom = [[sqlHelper alloc] init];
- [sqlCom executeInsertWithSql:@"INSERT INTO authorInfo(author,age) VALUES (?,?)",@"cheungching",@"25", nil nil];
- -(void)vaMethod:(id)object1, ...{
- va_list argList;
- id arg;
- if (object1) {
- va_start(argList, object1);
- while ((arg = va_arg(argList, id))) {
- NSLog(@"%@",arg);
- }
- va_end(argList);
- }
- }
-
- [self vaMethod:someObj,button,@"ss",nil];
注意第一个参数为object1,之后才是可变参数列表。
Disscussion:
--1.不定参数可以指定任何实际的类型,(id) 可真是任何类型了;
--2.Objective-C 的不定参数,即 ... 也必须放在函数的最后面,如还有其他参数时,foo 要写成:
- - (void)fooState: (BOOL) enable withFormat: (NSString *)format, ...
- - (void)fooFormat: (NSString *)format, ... withState: (BOOL) enable
--3.在调用的时候要不要在参数结尾的时候加 nil,回想下 [NSMutableArray arrayWithObjects: 1, 2, 3, nil] 这个构造过程,最后一个 nil 能让 va_arg 取参数时碰到 nil 则断定为 NO,终止循环。为何像 NSLog 调用不需要最后一个 nil?Because -stringWithFormat: and NSLog can infer the number of arguments based on their format strings (the first argument). -arrayWithObjects: can't.
总结:在objective-c中使用可变参数格式为:(id)object1, ... 一般情况下object1为NSString类型,然后在方法内部使用va_start、va_arg等获取参数。至于调用的时候加不加nil,依赖于方法的实现,如果方法中以参数是否为nil作为结束条件(arg = va_arg(argList, id)为nil则va_end),则调用时必须加nil结尾。另外方法后可加一些宏定义(例如NS_FORMAT_FUNCTION(1,2))确定方法的一些格式,编译的时候对代码加以验证。
------------------------------------------------------------------------------------------------------------------
参考:
1.http://unmi.cc/obejctive-c-var-arguments/ 《Obejctive-C 中定义可变参函数》
2.http://mobile.51cto.com/iphone-280106.htm 《Objective-C可变参数函数定义》
3.http://www.cnblogs.com/super119/archive/2011/04/05/2005592.html 《Using GNU C __attribute__ 阅读笔记》