我使用以下模式爲iOS庫編寫了一個具有c函數的Helper類。 有2個包裝(可變參數)函數,最終調用相同的函數,參數略有不同。想法是設置「默認」屬性。然後Variadic函數緩存最後調用的參數列表
__attribute__((overloadable)) void func1(NSString* _Nonnull format, ...);
__attribute__((overloadable)) void func1(int param1, NSString* _Nonnull format, ...);
雙方將調用下面的函數:
void prefixAndArguments(int param1, NSString* _Nonnull format, va_list arguments);
實現如下:
__attribute__((overloadable)) void func1(NSString* _Nonnull format, ...)
{
va_list argList;
va_start(argList, format);
prefixAndArguments(0, format, argList);
va_end(argList);
}
__attribute__((overloadable)) void func1(int param1, NSString* _Nonnull format, ...)
{
va_list argList;
va_start(argList, format);
prefixAndArguments(param1, format, argList);
va_end(argList);
}
void prefixAndArguments(NMXLogLevelType logLevel, NSString* _Nullable logPrefix, __strong NSString* _Nonnull format, va_list arguments)
{
// Evaluate input parameters
if (format != nil && [format isKindOfClass:[NSString class]])
{
// Get a reference to the arguments that follow the format parameter
va_list argList;
va_copy(argList, arguments);
int argCount = 0;
NSLog(@"%d",argCount);
while (va_arg(argList, NSObject *))
{
argCount += 1;
}
NSLog(@"%d",argCount);
va_end(argList);
NSMutableString *s;
if (numSpecifiers > argCount)
{
// Perform format string argument substitution, reinstate %% escapes, then print
NSString *debugOutput = [[NSString alloc] initWithFormat:@"Error occured when logging: amount of arguments does not for to the defined format. Callstack:\n%@\n", [NSThread callStackSymbols]];
printf("%s\n", [debugOutput UTF8String]);
s = [[NSMutableString alloc] initWithString:format];
}
else
{
// Perform format string argument substitution, reinstate %% escapes, then print
va_copy(argList, arguments);
// This is were the EXC_BAD_ACCESS will occur!
// Error: Thread 1: EXC_BAD_ACCESS (code=EXC_I386_GPFLT)
s = [[NSMutableString alloc] initWithFormat:format arguments:argList];
[s replaceOccurrencesOfString:@"%%"
withString:@"%%%%"
options:0
range:NSMakeRange(0, [s length])];
NSLog(@"%@",s);
va_end(argList);
}
...
}
的功能我的單元測試看看以下(順序很重要)。
// .. some previous cases, I commented out
XCTAssertNoThrow(NMXLog(@"Simple string output"));
XCTAssertNoThrow(NMXLog(@"2 Placeholders. 0 Vars %@ --- %@"));
的崩潰發生時,我想使用的參數和格式(製作格式強並沒有解決這個問題,似乎並沒有成爲問題的一部分,見下文):
s = [[NSMutableString alloc] initWithFormat:format arguments:argList];
下面是日誌:
xctest[28082:1424378] 0
xctest[28082:1424378] --> 1
xctest[28082:1424378] Simple string output
xctest[28082:1424378] 0
xctest[28082:1424378] --> 4
當然,我們不會看到所需的字符串作爲"2 Placeholders. 0 Vars %@ --- %@"
在飛機墜毀前發生的事情。
所以,現在的問題是:爲什麼參數的數量現在是4而不是0?由於沒有在第二次調用中傳遞,函數被立即再次調用時是否收集了參數?
於是,我開始爲「一次」調用的函數,以確保參數的列表被清除,儘管va_end
是被稱爲:
__attribute__((overloadable)) void func1(NSString* _Nonnull format, ...)
{
va_list argList;
va_start(argList, format);
prefixAndArguments(none, nil, format, argList);
va_end(argList);
NSString *obj = nil;
prefixAndArguments(none, nil, obj, nil);
}
這不現在的工作就像一個魅力(參數的列表是清除並正在接收所需的輸出):
xctest[28411:1453508] 0
xctest[28411:1453508] --> 1
xctest[28411:1453508] Simple string output
xctest[28411:1453508] 0
xctest[28411:1453508] --> 1
Error occured when logging: amount of arguments does not for to the defined format. Callstack: ....
xctest[28411:1453508] 2 Placeholders. 0 Vars %@ --- %@
這裏是我的最後一個問題:
這種行爲的原因是什麼?我該如何避免這種行爲?有沒有更好的方法來解決這個問題,而不是「愚蠢地」第二次用「無」參數調用函數來清除它們? P.s.我試圖不使用宏,因爲我認爲它們比c函數更容易出錯。看到這個線程:Macro vs Function in C
問問你自己:如何'va_arg'知道什麼時候停止? –
這似乎是通過實施可選的第一個參數來解決很多麻煩的問題。如何,而不是。 –
@JohnBollinger謝謝你在約翰。爲了簡單起見,我剛剛提到了兩個帶有一個可選參數的函數。當然,還有更多。 – Lepidopteron