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

来源:互联网 发布:qq音乐数据库 api接口 编辑:程序博客网 时间:2024/06/05 03:29
          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)的意思:

// NSObjCRuntime.h#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,哪个参数是参数列表,这样可以避免代码中的一些问题,比如:

/* like printf() but to standard error only */extern void eprintf(const char *format, ...)   __attribute__((format(printf, 1, 2)));  /* 1=format 2=params *//* printf only if debugging is at the desired level */extern void dprintf(int dlevel, const char *format, ...)   __attribute__((format(printf, 2, 3)));  /* 2=format 3=params */
         从上面可以看出,我们定义了eprintf函数,第一个参数是Format String,第二个参数是对应Format String的参数列表,下面的dprintf也是一样,这样一来,编译器就能检测出下面这样的代码错误:
1  extern void eprintf(const char *format, ...)2               __attribute__((format(printf, 1, 2)));34  void foo()5  {6      eprintf("s=%s\n", 5);             /* error on this line */78      eprintf("n=%d,%d,%d\n", 1, 2);    /* error on this line */9  }$ cc -Wall -c test.ctest.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;    //使用第一个参数来初使化list指针    va_start(list, value);    while (YES)    {        //返回可变参数,va_arg第二个参数为可变参数类型,如果有多个可变参数,依次调用可获取各个参数        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;            }  // 在调用的时候要在参数结尾的时候加 nilsqlHelper *sqlCom = [[sqlHelper alloc] init];  [sqlCom executeInsertWithSql:@"INSERT INTO authorInfo(author,age) VALUES (?,?)",@"cheungching",@"25", 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__ 阅读笔记》

 

0 0
原创粉丝点击