1

基于几个小时前提出的这个SO 问题,我决定实现一个 swizzled 方法,该方法允许我将格式化NSString为 arg 格式stringWithFormat,并且在省略一个编号的 arg 引用 ( %1$@, %2$@)时不会中断

我有它的工作,但这是第一个副本,并且看到这个方法可能会在每次应用程序运行时被调用数十万次,我需要从一些专家那里反弹,看看这个方法是否有任何危险信号, 主要性能影响或优化

#define NUMARGS(...)  (sizeof((int[]){__VA_ARGS__})/sizeof(int))
@implementation NSString (UAFormatOmissions)
+ (id)uaStringWithFormat:(NSString *)format, ... {  
    if (format != nil) {
        va_list args;
        va_start(args, format);

        // $@ is an ordered variable (%1$@, %2$@...)
        if ([format rangeOfString:@"$@"].location == NSNotFound) {
            //call apples method
            NSString *s = [[[NSString alloc] initWithFormat:format arguments:args] autorelease];
            va_end(args);
            return s;
        }

        NSMutableArray *newArgs = [NSMutableArray arrayWithCapacity:NUMARGS(args)];
        id arg = nil;
        int i = 1;
        while (arg = va_arg(args, id)) {
            NSString *f = [NSString stringWithFormat:@"%%%d\$\@", i];
            i++;
            if ([format rangeOfString:f].location == NSNotFound) continue;
            else [newArgs addObject:arg];
        }
        va_end(args);

        char *newArgList = (char *)malloc(sizeof(id) * [newArgs count]);
        [newArgs getObjects:(id *)newArgList];
        NSString* result = [[[NSString alloc] initWithFormat:format arguments:newArgList] autorelease];
        free(newArgList);
        return result;
    }
    return nil;
}

基本算法是:

  1. %1$@通过搜索来搜索,%2$@变量的格式字符串%@
  2. 如果没有找到,调用正常的 stringWithFormat 并返回
  3. 否则,遍历 args
  4. 如果格式具有%i$@位置 i 的位置变量 ( ),则将 arg 添加到新的 arg 数组
  5. 否则,不要添加 arg
  6. 获取新的 arg 数组,将其转换回 a va_list,然后调用initWithFormat:arguments:以获取正确的字符串。

这个想法是我将[NSString stringWithFormat:]通过此方法运行所有调用。

这对许多人来说似乎是不必要的,但请单击引用的 SO 问题(第一行)以查看我为什么需要这样做的示例。

想法?想法?更好的实现?更好的解决方案?

4

2 回答 2

3

哇!

与其使用您很可能会引入细微错误的核心方法,不如在您的项目选项中打开“静态分析器”,它将运行每个构建 - 如果您输入错误的参数,它将发出编译器警告你。

我感谢您希望使应用程序更健壮的愿望,但我认为重写此方法很可能会破坏您的应用程序而不是保存它。

于 2010-06-01T04:02:43.833 回答
1

如何定义自己的临时方法而不是使用格式说明符和stringWithFormat:?例如,您可以定义自己的方法replaceIndexPoints:来查找($1)而不是%1$@. 然后,您将格式化您的字符串并独立插入翻译的替换。此方法还可以采用字符串数组,NSNull在“未翻译”字符串中不存在的索引处包含或为空字符串。

您的方法可能如下所示(如果它是 的类别方法NSMutableString):

- (void) replaceIndexPointsWithStrings:(NSArray *) replacements
{
    // 1. look for largest index in "self".
    // 2. loop from the beginning to the largest index, replacing each
    //    index with corresponding string from replacements array.
}

以下是我在您当前的实现中看到的一些问题(一目了然):

  1. __VA_ARGS__评论中解释的事情。
  2. 当您使用 时while (arg = va_arg(args, id)),您假设参数已nil终止(例如 for arrayWithObjects:),但这stringWithFormat:不是必需的。
  3. 我认为您不需要在 arg 循环中以字符串格式转义$and @
  4. 我不确定如果uaStringWithFormat:传递了比指针更大的东西(即long long如果指针是 32 位的),这是否会正常工作。如果您的翻译还需要插入未本地化的数量级,这可能只是一个问题long long
于 2010-06-01T00:54:19.353 回答