iOS:在objective-c 使用可变参数

 objective-c中已有的一些可变参数方法:
[objc]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. FOUNDATION_EXPORT  void NSLog(NSString *format, ...) NS_FORMAT_FUNCTION(1,2);  
  2. - (instancetype)initWithFormat:(NSString *)format, ... NS_FORMAT_FUNCTION(1,2);  
  3. + (instancetype)arrayWithObjects:(id)firstObj, ... NS_REQUIRES_NIL_TERMINATION; // 注意区别  

          在c/c++中使用可变参数参考《C/C++ 使用可变参数 & 原理》,在objective-c中使用情况也差不多,一些宏定义说明如下:

----------------------------------------------------------------------------------------------------------------------------------

       NS_FORMAT_FUNCTION(1,2)的意思:

[objc]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. // NSObjCRuntime.h  
  2. #if !defined(NS_FORMAT_FUNCTION)  
  3.     #if (__GNUC__*10+__GNUC_MINOR__ >= 42) && (TARGET_OS_MAC || TARGET_OS_EMBEDDED)  
  4.     #define NS_FORMAT_FUNCTION(F,A) __attribute__((format(__NSString__, F, A)))  
  5.     #else  
  6.     #define NS_FORMAT_FUNCTION(F,A)  
  7.     #endif  
  8. #endif  

           __attribute__,是GNU编译器的一个特性,这个宏是一个编译器指令,我们在代码中通过定义这个东西,可以inform编译器我们代码 的一些逻辑,从而在编译器避免一些错误,在运行期提高性能。__attribute__在很多代码中都有应用,非常实用。

          __attribute__ format ,这个东西能告知编译器,我们的代码在处理printf,scanf这样变参数的函数的时候,哪个参数是format string,哪个参数是参数列表,这样可以避免代码中的一些问题,比如:

[objc]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. /* like printf() but to standard error only */  
  2. extern void eprintf(const charchar *format, ...)  
  3.    __attribute__((format(printf, 12)));  /* 1=format 2=params */  
  4.   
  5. /* printf only if debugging is at the desired level */  
  6. extern void dprintf(int dlevel, const charchar *format, ...)  
  7.    __attribute__((format(printf, 23)));  /* 2=format 3=params */  
         从上面可以看出,我们定义了eprintf函数,第一个参数是Format String,第二个参数是对应Format String的参数列表,下面的dprintf也是一样,这样一来,编译器就能检测出下面这样的代码错误:
[objc]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. 1  extern void eprintf(const charchar *format, ...)  
  2. 2               __attribute__((format(printf, 12)));  
  3. 3  
  4. 4  void foo()  
  5. 5  {  
  6. 6      eprintf("s=%s\n"5);             /* error on this line */  
  7. 7  
  8. 8      eprintf("n=%d,%d,%d\n"12);    /* error on this line */  
  9. 9  }  
  10.   
  11. $ cc -Wall -c test.c  
  12. test.c: In function `foo':  
  13. test.c:6: warning: format argument is not a pointer (arg 2)  
  14. test.c:8: warning: too few arguments for format  
         其他一些 __attribute__ 特性: __attribute__ const,  这个东西能告诉编译器, 在给定参数的情况下,这个function始终返回同样的值 。这样可以帮助程序提高性能,比如:
[objc]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. extern int square(int n) __attribute__((const));  
  2. ...  
  3.    for (i = 0; i < 100; i++ )  
  4.    {  
  5.       total += square(5) + i;  
  6.    }  
        如果我们在square函数中没有定义__attribute__ const的话,在下面的那个循环中,程序每次都要产生一个调用square函数的代码。但是这里指定了const之后,程序就知道,对于同一个输入参数 5,返回值都是一样的。 这样程序就会执行一次square,然后cache这个函数的return value ,这样下次循环开始,对square函数的调用就没有函数调用的逻辑了,直接返回上次的结果

----------------------------------------------------------------------------------------------------------------------------------

       在objective-c中使用可变参数的例子:

[objc]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. - (void)foo:(NSString *)format, ...  
  2. {  
  3.     va_list args;  
  4.     va_start(args, format);  
  5.     NSString *str = [[NSString alloc] initWithFormat:format arguments:args];  
  6.     va_end(args);   
  7.     printf([str UTF8String]);   
  8.     [str release];  
  9. }    
  10. - (IBAction) doo: (UIButton*) sender  
  11. {  
  12.     //须留意不定参函数的调用格式,逗号分隔的序列,应该它们整体是作为函数的一个参数传入  
  13.     [self foo : @"My name %@, %@"@"Unmi"@"Yes"];  
  14. }  
[objc]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. - (void)method:(NSString *)value,...  
  2. {  
  3.     //指向变参的指针  
  4.     va_list list;  
  5.     //使用第一个参数来初使化list指针  
  6.     va_start(list, value);  
  7.     while (YES)  
  8.     {  
  9.         //返回可变参数,va_arg第二个参数为可变参数类型,如果有多个可变参数,依次调用可获取各个参数  
  10.         NSString *string = va_arg(list, NSString*);  
  11.         if (!string) {  
  12.             break;  
  13.         }  
  14.         NSLog(@"%@",string);  
  15.     }  
  16.     //结束可变参数的获取  
  17.     va_end(list);  
  18. }  

        函数调用:[self method:@"1",@"2",@"3",nil];像大多数变参函数一样,未尾一定要加上nil,因为这一组宏都没有提供对参数个数的检测,当然你可以会问为何NSLog的参数中我们都不用在末尾添加nil的参数呢,那是因为NSLog的第一个参数是一个格式化字符串,通过这个字条串就能获得后面的参数个数,所以如果你的函数还能有其它的参数能够显式的指出变参个数,当然你也可以书写(但在函数体中需要修改为按已知个数调用va_arg),仍然推荐以上的写法

[objc]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. @interface sqlHelper : NSObject    
  2.  {    
  3.  }    
  4.  -(int) executeInsertWithSql:(NSString *) statement, ...;    
  5. @end    
  6.      .m文件    
  7. -(int) executeInsertWithSql:(NSString *) statement, ...    
  8. {    
  9.     PLSqliteDatabase* dbPointer = [SqliteDataBase setUp];    
  10.     argsArray = [[NSMutableArray alloc] init];    
  11.     id arg;    
  12.     va_list argList;    
  13.     if(statement)    
  14.             {    
  15.                    va_start(argList,statement);    
  16.                    while (arg = va_arg(argList,id))    
  17.                    {    
  18.                          [argsArray addObject:arg];    
  19.                    }    
  20.                    va_end(argList);    
  21.             }     
  22.                        BOOL bResult = [dbPointer executeUpdate:statement,[argsArray objectAtIndex:0],[argsArray  objectAtIndex:1]];    
  23.              return bResult;    
  24.           }    
  25. // 在调用的时候要在参数结尾的时候加 nil  
  26. sqlHelper *sqlCom = [[sqlHelper alloc] init];    
  27. [sqlCom executeInsertWithSql:@"INSERT INTO authorInfo(author,age) VALUES (?,?)",@"cheungching",@"25", nil nil];   

[objc]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. -(void)vaMethod:(id)object1, ...{  
  2.     va_list argList;  
  3.     id arg;  
  4.     if (object1) {  
  5.         va_start(argList, object1);  
  6.         while ((arg = va_arg(argList, id))) {  
  7.             NSLog(@"%@",arg);  
  8.         }  
  9.         va_end(argList);  
  10.     }  
  11. }  
  12. // 调用  
  13. [self vaMethod:someObj,button,@"ss",nil];  

        注意第一个参数为object1,之后才是可变参数列表。

Disscussion:      

--1.不定参数可以指定任何实际的类型,(id) 可真是任何类型了;

--2.Objective-C 的不定参数,即 ... 也必须放在函数的最后面,如还有其他参数时,foo 要写成:

[objc]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. - (void)fooState: (BOOL) enable withFormat: (NSString *)format, ...    
  2. - (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__ 阅读笔记》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值