2009-01-10 44 views
12

在蟒很容易构建一个字典或数组,并将它传递解压到具有可变参数是否可以在Obj-c中为可变参数函数发送一个数组?

函数我已经这样:

- (BOOL) executeUpdate:(NSString*)sql, ... { 

和手动方式是这样的:

[db executeUpdate:@"insert into test (a, b, c, d, e) values (?, ?, ?, ?, ?)" , 
    @"hi'", // look! I put in a ', and I'm not escaping it! 
    [NSString stringWithFormat:@"number %d", i], 
    [NSNumber numberWithInt:i], 
    [NSDate date], 
    [NSNumber numberWithFloat:2.2f]]; 

但我不能硬编码我打电话的参数,我想:

NSMutableArray *values = [NSMutableArray array]; 

for (NSString *fieldName in props) { 
    .. 
    .. 
    [values addObject : value] 
} 
[db executeUpdate:@"insert into test (a, b, c, d, e) values (?, ?, ?, ?, ?)" ,??values]; 

回答

15

不幸的是,没有。 Objective-C没有像许多现代语言一样的拆箱操作。我甚至找不到解决这个问题的好办法。

问题的部分原因是Objective-C本质上只是C.它使用C可变参数传递多个参数,并且没有简单的方法来使用可变参数来执行此操作。 A relevant SO discussion

+0

我刚从另一篇文章中发现,如果需要,您可以根据需要创建此功能,虽然Cocoa默认情况下没有这样的方法。 http://stackoverflow.com/questions/1058736/#1061750 – 2009-06-30 13:39:00

+1

自从我调查了这一段已经有一段时间了,但我认为创建任意可变参数列表的方法有点脆弱。这取决于不能保证的实施细节。只是一个警告。 – Chuck 2009-06-30 15:48:50

1

要完成您想要的任务,您必须使用“可变参数”,因为您的方法正在使用,或者您可以传入一组值,如[db executeUpdate:sql withValues:vals];,然后提取方法中的值。但是没有办法做更多的“Pythonic”,比如自动解压缩一个值的元组,这就是“012th”。

1

不幸的是(Objective-)C没有提供这样做的方法。在这种情况下,executeUpdate方法需要接受一个NSArray而不是可变参数列表。

但是,如果你知道条目的数组(你有反正例如字符串中的金额),你当然可以这样做

[db executeUpdate:@"insert into test (a, b) values (?, ?)", [values objectAtIndex:0], [values objectAtIndex:1]] 

如果的executeUpdate是一个外部的量库方法和该库不提供接受NSArray的方法的版本,你可以拿出你自己的包装函数。该函数将查询字符串和数组作为参数。那么该功能将调用基于阵列的长度的参数正确的金额,东西executeUpdate方法沿

if ([values count] == 1) { 
    [db executeUpdate:query, [values objectAtIndex:0]]; 
} 
else if ([values count] == 2) { 
    [db executeUpdate:query, [values objectAtIndex:0], [values objectAtIndex:1]]; 
} 

行,那么你可以把这种新的功能

executeUpdateWrapper(@"insert into test (a, b) values (?, ?)", values); 

明显这个解决方案的缺点是你需要在函数中单独处理所有可能的数组长度,并且它有很多复制粘贴代码。

4

我想做同样的事情。我想出了以下内容,在输入变量有一些限制的情况下,它工作正常。

NSArray* VarArgs(va_list ap) 
{ 
    id obj; 
    NSMutableArray* array = [NSMutableArray array]; 

    while ((obj = va_arg(ap, id))) { 
    [array addObject:obj]; 
    } 
    return array; 
} 

#define VarArgs2(_last_) ({ \ 
    va_list ap; \ 
    va_start(ap, _last_); \ 
    NSArray* __args = VarArgs(ap); \ 
    va_end(ap); \ 
    if (([__args count] == 1) && ([[__args objectAtIndex:0] isKindOfClass:[NSArray class]])) { \ 
    __args = [__args objectAtIndex:0]; \ 
    } \ 
__args; }) 

使用上面,我可以调用NSArray或可变参数以下。

// '...' must be objc objects with nil sentinel OR an NSArray with nil sentinel 
- (void)someMethod:(NSString *)sql, ... 
{ 
    NSArray *args = VarArgs2(sql); 

    // Do stuff with args 
} 

一个提示是使用下面的原型有对无定点编译器检查,以避免潜在的不好的东西。我从苹果标题中得到了这个...

- (void)someMethod:(NSString *)sql, ... NS_REQUIRES_NIL_TERMINATION; 
+1

NS_REQUIRES_NIL_TERMINATION是一个很好的提示。谢谢。 – 2009-03-16 17:47:43

3

有一个很好的例子,你可以如何从NSArray中去这里的va_list(参见“可可va_list的”和“创建一个假的va_list的”向底部部分):

http://cocoawithlove.com/2009/05/variable-argument-lists-in-cocoa.html

这里是一个传情( “论据” 是的NSArray):

char *argList = (char *)malloc(sizeof(NSString *) * [arguments count]); 
[arguments getObjects:(id *)argList]; 
contents = [[NSString alloc] initWithFormat:formatString arguments:argList]; 
free(argList); 

不太Python或Ruby,但嘿...

2

您应该使用新的FMDB版本http://github.com/ccgus/fmdb。它的方法,你需要:

- (BOOL) executeUpdate:(NSString*)sql withArgumentsInArray:(NSArray *)arguments; 
0

附加到robottobor的解决方案: 如果你添加下面的宏:

#define splitAlternatingArray(args,arg1,arg2) \ 
NSMutableArray *arg1 = [NSMutableArray array];\ 
NSMutableArray *arg2 = [NSMutableArray array];\ 
{\ 
    BOOL isFirst = YES;\ 
    for (id arg in args) {\ 
    if (isFirst) {\ 
     [arg1 addObject:arg];\ 
    } else {\ 
     [arg2 addObject:arg];\ 
    }\ 
    isFirst = !isFirst;\ 
    }\ 
} 

然后你可以做技巧性的东西,如:

- (id)initWithObjectsAndKeys:(id)firstObject, ...{ 
    NSArray *objKeyArray = VarArgs2(firstObject); 
    splitAlternatingArray(objKeyArray,objs,keys); 
    return [self initWithObjects:objs forKeys:keys]; 
} 
16

查是对的,在Objective-C中没有合适的参数解包。但是,对于需要零终止的方法(NS_REQUIRES_NIL_TERMINATION),您可以扩大变量列表,使其大于使用返回nil的数组访问器(index >= count)所需的变量列表。这当然是一个黑客,但它的工作。

// Return nil when __INDEX__ is beyond the bounds of the array 
#define NSArrayObjectMaybeNil(__ARRAY__, __INDEX__) ((__INDEX__ >= [__ARRAY__ count]) ? nil : [__ARRAY__ objectAtIndex:__INDEX__]) 

// Manually expand an array into an argument list 
#define NSArrayToVariableArgumentsList(__ARRAYNAME__)\ 
NSArrayObjectMaybeNil(__ARRAYNAME__, 0),\ 
NSArrayObjectMaybeNil(__ARRAYNAME__, 1),\ 
NSArrayObjectMaybeNil(__ARRAYNAME__, 2),\ 
NSArrayObjectMaybeNil(__ARRAYNAME__, 3),\ 
NSArrayObjectMaybeNil(__ARRAYNAME__, 4),\ 
NSArrayObjectMaybeNil(__ARRAYNAME__, 5),\ 
NSArrayObjectMaybeNil(__ARRAYNAME__, 6),\ 
NSArrayObjectMaybeNil(__ARRAYNAME__, 7),\ 
NSArrayObjectMaybeNil(__ARRAYNAME__, 8),\ 
NSArrayObjectMaybeNil(__ARRAYNAME__, 9),\ 
nil 

现在你可以使用NSArrayToVariableArgumentsList无论你期待一个无终止的变量参数列表(只要你的阵列小于10种元素)。例如:

NSArray *otherButtonTitles = @[@"button1", @"button2", @"button3"]; 
UIActionSheet *actionSheet = [[self alloc] initWithTitle:@"Title" 
               delegate:self 
             cancelButtonTitle:@"Cancel" 
            destructiveButtonTitle:nil 
             otherButtonTitles:NSArrayToVariableArgumentsList(otherButtonTitles)]; 
相关问题