1

数時間前に尋ねられたこのSOの質問に基づいて、フォーマットさNSStringれたargをに取り込んstringWithFormatで、番号付きのarg参照の1つを省略しても壊れないようにするswizzledメソッドを実装することにしました( %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の質問(最初の行)をクリックして、これを行う必要がある理由の例を確認してください。

アイデア?考え?より良い実装?より良いソリューション?

4

2 に答える 2

3

おっと!

微妙なバグが発生する可能性が非常に高いコアメソッドをねじ込む代わりに、プロジェクトオプションで「StaticAnalyzer」をオンにすると、すべてのビルドが実行されます。引数を間違えると、コンパイラの警告が発行されます。君。

アプリケーションをより堅牢にしたいというあなたの願望に感謝しますが、このメソッドを書き直すと、アプリケーションを保存するよりも壊してしまう可能性が非常に高いと思います。

于 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-loopでとを文字列形式でエスケープする必要はない$と思います。@
  4. uaStringWithFormat:ポインタよりも大きいものが渡された場合(つまりlong long、ポインタが32ビットの場合)、これがうまく機能するかどうかはわかりません。long longこれは、翻訳でローカライズされていない大きさの数値を挿入する必要がある場合にのみ問題になる可能性があります。
于 2010-06-01T00:54:19.353 に答える