2009-01-10 18 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)]; 
相關問題