代码之家  ›  专栏  ›  技术社区  ›  coneybeare

nsstring stringwithformat已切换为允许缺少格式编号的参数

  •  1
  • coneybeare  · 技术社区  · 14 年前

    基于 this 所以几个小时前我问了这个问题,我决定实现一个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. 否则,循环参数
    4. 如果格式有位置变量( %i$@ )对于位置i,将arg添加到新arg数组中
    5. 否则,不要添加参数
    6. 获取新的arg数组,将其转换回 va_list 和呼叫 initWithFormat:arguments: 得到正确的字符串。

    我的想法是,我会全力以赴 [NSString stringWithFormat:] 而是通过此方法调用。

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

    思想?思想?更好的实施?更好的解决方案?

    2 回复  |  直到 14 年前
        1
  •  1
  •   dreamlax    14 年前

    如何定义自己的临时方法而不是使用格式说明符和 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__ Thingy在评论中解释道。
    2. 当你使用 while (arg = va_arg(args, id)) ,您假设参数是 nil 终止(例如 arrayWithObjects: ) 字符串格式: 这不是要求。
    3. 我认为你不需要逃避 $ @ 在arg循环的字符串格式中。
    4. 我不确定如果 uaStringWithFormat: 传递的对象大于指针(即 long long 如果指针是32位)。这可能只是一个问题,如果您的翻译还需要插入不适用的数字 长长 震级。
        2
  •  3
  •   Kendall Helmstetter Gelner    14 年前

    哇!

    不要纠结于一个你很可能会引入细微错误的核心方法,而是在你的项目选项中打开“静态分析器”,它将运行每一个构建——如果你的参数错误,它将为你发出一个编译器警告。

    我很感激您希望使应用程序更健壮,但我认为重新编写此方法比保存它更有可能破坏您的应用程序。

    推荐文章