我使用以下模式为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