我想構建一個與NSString
的stringWithFormat:
(帶有可變參數)相同簽名的函數,但是我希望在將每個參數傳遞給stringWithFormat:
之前對URL進行編碼本身。我已經有一個編碼方法+ (NSString *)urlEncode:(NSString *)s
。如何使用編碼參數構建va_list?在傳遞它們之前處理可變參數
回答
我覺得這很難做得很好。事情是,你不能創建一個va_list
; 「功能」va_start
,va_end
等被定義爲宏,隱藏醜惡的內部東西,你可能不想讓你的手髒。
如果你真的想這樣做,你很可能解析format
字符串的內容(不應該是非常困難的,特別是如果你只承擔%@
是可能的格式說明符),讓你知道有多少參數有在va_list
,然後encode
他們每個人並返回最終重建字符串。不過,你當然要重新實現stringWithFormat:然後。
「互聯網」告訴我有構建va_list
沒有可移植的方法,但是如果可以的話,你就可以使用以下的想法:
s = [[[NSString alloc] initWithFormat:format arguments:argp] autorelease];
我個人認爲最好的辦法就是到每當遇到%@
令牌時,請穿過字符串並建立結果,使用va_arg
。
這是關於@daxnitro在下面說的,我建議的方法,他稱爲選項3:這是一個快速入侵,但僅用於對象令牌,它的工作原理如下。
typedef enum {
NORMAL,
TOKEN
} STATE;
+ (NSString *) encodeWithFormat: (id) format, ... {
STATE s = NORMAL;
va_list argp;
va_start(argp, format);
unichar c;
NSString * tmp;
NSMutableString * out = [[NSMutableString alloc] init];
for(int i = 0; i < [format length]; i ++) {
c = [format characterAtIndex: i];
// simple state-based recognising
switch(c) {
case '%':
if(s == NORMAL)
s = TOKEN; // switch to token-mode
else { // we were accepting tokens, so this is an escaped '%'
[out appendFormat: @"%c", c];
s = NORMAL;
}
break;
default:
if(s == NORMAL) // default case
[out appendFormat: @"%c", c];
else // accepting tokens, so check type
switch(c) {
case '@': // this is a string placeholder
tmp = va_arg(argp, NSString*);
[out appendFormat: @"%@", [Test encode:tmp]]; // your magic here
s = NORMAL;
break;
// you could add cases for %d etc here, if necessary
default: // some unrecognised placeholder. ignore.
s = NORMAL;
break;
}
break;
}
}
va_end(argp);
return [out autorelease];
}
在C,C++或Objective-C中沒有可移植的方法來構建va_lists。你可能可以編寫或查找一些使用內聯彙編的函數來直接修改堆棧並調用可變參數,但這不是一個好的方法。你有三個我能想到的實用選項。
這是第一個選項。僅使用可變字符串,並使用NSString的initWithFormat:arguments:方法轉發參數。
- (NSString*)forwardMessage:(NSString*)format, ... {
va_list args;
va_start(args, format);
BOOL escape = NO;
char* ptr = (char*)[format UTF8String];
while (*ptr) {
if (*ptr == '%') {
escape = !escape;
} else if (escape) {
// argument
id obj = va_arg(args, id);
if (*ptr == '@') {
// object
if ([obj isKindOfClass:[NSString class]]) {
// string
id copy = [obj copy];
if (copy != obj) {
// mutable
[obj replaceCharactersInRange:NSMakeRange(0, [obj length]) withString:@"replaced!"];
}
}
}
escape = NO;
}
++ptr;
}
va_end(args);
va_list args2;
va_start(args2, format);
NSString* ret = [[NSString alloc] initWithFormat:format arguments:args2];
va_end(args2);
return ret;
}
該方法將採用可變參數,並用「replacement!」替換任何可變字符串的內容。由於我們只能在轉發它們之前讀取參數,因此我們實際上不能將不同的對象發送到initWithFormat:參數:我們只需更改對象。請注意,製作對象的副本以測試它是否像我在該方法中做的那樣是可變的,這不是很好的做法。
這是您的第二個選項。使用NSInvocation構建您的新參數到stringWithFormat :.
- (NSString*)forwardMessage:(NSString*)format, ... {
BOOL escape = NO;
NSUInteger count = 0;
char* ptr = (char*)[format UTF8String];
while (*ptr) {
if (*ptr == '%') {
escape = !escape;
} else if (escape) {
if (*ptr == '@') {
// this is an object
}
++count;
escape = NO;
}
++ptr;
}
char* sig = malloc(3 + count + 2);
memset(sig, '@', 3 + count);
sig[3 + count] = ':';
sig[3 + count + 1] = '\0';
NSInvocation* invocation = [NSInvocation invocationWithMethodSignature:[NSMethodSignature signatureWithObjCTypes:sig]];
free(sig);
[invocation setTarget:[NSString class]];
[invocation setSelector:@selector(stringWithFormat:)];
[invocation setArgument:&format atIndex:2];
va_list args;
va_start(args, format);
for (NSUInteger i = 0; i < count; ++i) {
void* arg = va_arg(args, void*);
// arg is an object, you can change it here
[invocation setArgument:&arg atIndex:i + 3];
}
[invocation invoke];
va_end(args);
id ret;
[invocation getReturnValue:&ret];
return ret;
}
這種方法有一個缺點:當你通過具有尺寸類型是不一樣的無效*或ID它不會工作。例如,整數有效,但浮點數不正確。 [self forwardMessage:@"test %d asd %s %f %@ %d", 2, "asd", 2.897, @"test" 5]
返回test 2 asd asd 0.000000 test 5
。添加更多邏輯以使特定類型正常工作並不難。
我不確定我真的可以推薦這些解決方案,但也許他們會爲你的情況工作。
第三個選項:如果你的參數只是NSString對象,我將不得不建議放棄stringWithFormat:並自己解析/形成格式化的字符串。
- 1. Rails:在控制器中處理它們之前驗證參數?
- 2. JavaScript在函數之間傳遞變量並對它們進行處理
- 3. 在處理它們之前更新POST數據的PHP數組
- 4. 傳遞批處理參數
- 5. 我可以建立在它們之間NUnit的3傳遞參數
- 6. 在將數據傳遞給SSRS 2005之前,我可以預處理數據嗎?
- 7. 參數傳遞到apscheduler處理函數
- 8. 如何在變量處理之前將變量傳遞給表單
- 9. 命名參數與傳遞給它們的變量相同嗎?
- 10. 傳遞參數的AsyncTask,並改變它們
- 11. 在頁面之間傳遞多個變量並使用它們
- 12. 傳遞額外的參數來處理
- 13. 處理多個傳遞的參數
- 14. 參數傳遞批處理文件
- 15. 傳遞VB參數批處理文件
- 16. 強制JDialog在處理數據之前變得可見?
- 17. 傳遞變量參數在C++可變參數模板
- 18. 有沒有辦法在傳遞給基類之前處理傳遞給子類構造函數的變量?
- 19. 將可變參數傳遞到數組
- 20. 傳遞可變數量的參數
- 21. .Net - 傳遞可變數量的參數
- 22. 在location.reload之前傳遞一個變量()
- 23. 在傳遞給JSP標記處理程序之前對變量進行評估
- 24. 從參數中傳遞變量從vbscript到批處理文件
- 25. CodeIgniter路由:在控制器之前傳遞參數並在default_controller上捕獲它
- 26. 如何處理錯誤在射頻可變參數之後的位置參數?
- 27. 如何通過AJAX傳遞多個CheckBox值並處理它們?|
- 28. 在傳遞給jQuery之前清理字符串ajax數據參數
- 29. 將可變參數傳遞給方法
- 30. 有條件地傳遞可變參數
[Objective-C傳遞... nil終止參數列表]的可能重複(http://stackoverflow.com/questions/2345196/objective-c-passing-around-nil-terminated-argument-lists) – 2012-03-07 20:11:18