我编写了一种方法,可以使用格式字符串轻松设置URL的路径部分。最初,我只是将格式字符串和args直接传递给initWithFormat:
,但有人开始传递给我带有空格的args。在转到initWithFormat:
之前,我将方法更改为对参数进行百分比编码。
我可以将其称为[request setUrlWithFormat:@"users/%@/timesheets", username]
,其中username
可以是bmauter
或b mauter
。
- (void) setUrlWithFormat:(NSString *)format, ... {
// loop through varargs and cleanse them for the URL path
va_list args;
va_start(args, format);
NSMutableArray *cleaned = [[NSMutableArray alloc] init];
for(NSString *s = format; s != nil; s = va_arg(args, NSString *)) {
if (s == format) continue;
[cleaned addObject:[s stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLPathAllowedCharacterSet]]];
}
va_end(args);
// put the cleansed values back into a varargs list
__unsafe_unretained id *argList = (__unsafe_unretained id *) calloc(1UL, sizeof(id) * cleaned.count);
for (NSInteger i = 0; i < cleaned.count; i++) argList[i] = cleaned[i];
NSString* result = [[NSString alloc] initWithFormat:format, *argList];
free(argList);
[self setUrl:result];
}
有时我在第一个for
循环行上使用EXC_BAD_ACCESS崩溃。有时我会在initWithString:
行崩溃。大部分时间它都很完美。
更新:再次感谢@uliwitness。如果其他人想看到我最终得到的东西,请点击:
- (void) setUrlWithFormat:(NSString *)format, ... {
DLog(@"format=%@", format);
va_list args;
va_start(args, format);
NSMutableString *result = [format mutableCopy];
NSRange range = [result rangeOfString:@"%@"];
while(range.location != NSNotFound) {
NSObject *obj = va_arg(args, NSObject *);
NSString *dirty = nil;
if ([obj isKindOfClass:[NSString class]]) dirty = (NSString *)obj;
else dirty = [NSString stringWithFormat:@"%@", obj];
NSString *clean = [dirty stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLPathAllowedCharacterSet]];
DLog(@"dirty=%@, clean=%@", dirty, clean);
[result replaceCharactersInRange:range withString:clean];
range = [result rangeOfString:@"%@"];
}
va_end(args);
DLog(@"result=%@", result);
[self setUrl:result];
}
答案 0 :(得分:2)
你在这里做了几个假设,但事实并非如此。首先,你没有把NIL作为一个vararg,所以你不能假设会有一个NIL。相反,你必须计算格式占位符的数量,并且只抓住那么多的varargs。
您现在正在做的是在参数列表的末尾运行。有时你很幸运,这个列表后面的随机内存结果是8个零字节,所以它看起来像NIL而你的循环终止了。当它崩溃时,你得到一些其他随机字节,这些字节不是有效的对象指针,或者看起来像是指向不同类对象的指针,这就是你崩溃的原因。
另外,为什么你假设你可以将数组的第一项传递给NSString&#39; s -stringWithFormat :?您的代码恰好适用于此测试用例,但这只是因为您只有一个格式占位符。
-initWithFormat:将匹配数量的参数作为其格式字符串。没有不同的数字(就像你现在传递的那样),而不是数组(你可能认为你正在传递,但是因为C中的数组是指向第一项的指针,而C不能告诉指向一个对象的指针从指向数组的指针,并且不知道数组有多长,你实际传递的只是一个项目。
要完成这项工作,您需要做的就是编写自己的格式字符串解析版本。一个快速和脏的版本将是使用rangeOfString来查找"%@"
,然后将之前的部分附加到字符串,然后将转义的相应参数附加,并且一旦你的循环结束,剩下的就是字符串。