gpt4 book ai didi

objective-c - 在传递可变参数之前处理它们

转载 作者:搜寻专家 更新时间:2023-10-30 19:46:40 26 4
gpt4 key购买 nike

我想构建一个与 NSStringstringWithFormat: 具有相同签名的函数(使用可变参数),但我想对 URL 进行编码在将每个参数传递给 stringWithFormat: 本身之前。我已经有一个方法 + (NSString *)urlEncode:(NSString *)s 进行编码。如何使用编码参数构建 va_list?

最佳答案

在 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;
}

该方法将采用可变参数并将任何可变字符串的内容替换为“replaced!”。由于我们只能在转发参数之前读取参数,所以实际上我们不能将不同的对象发送到 initWithFormat:arguments:我们只需要更改对象即可。请注意,像我在该方法中所做的那样制作对象的副本来测试它是否可变并不是很好的做法。

这是您的第二个选择。使用 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;
}

该方法确实有一个缺点:当您传递大小与 void* 或 id 不同的类型时,该方法将不起作用。例如,整数有效,但 float 不正确。 [self forwardMessage:@"test %d asd %s %f %@ %d", 2, "asd", 2.897, @"test"5] 返回 test 2 asd asd 0.000000测试 5。不过,添加更多逻辑以使特定类型正常工作并不会太难。

我不确定我是否真的可以推荐这两种解决方案中的任何一种,但也许它们适合您的情况。

第三种选择:如果你的参数只是 NSString 对象,我将不得不建议放弃 stringWithFormat: 并自己解析/形成格式化的字符串。

关于objective-c - 在传递可变参数之前处理它们,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9599544/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com