1

を操作することNSMutableAttributedStringが私のアプリケーションのボトルネックであると判断しました。の構築をスピードアップするために何ができNSMutableAttributedStringますか?StringBuilderたとえば、Javaに相当するものはありますか?

appendFormat最大の犯人です。

編集:要求に応じて例。場合によっては、絶対にストレート連結を使用できます。私はそれが速いとは思いもしませんでした。

NSMutableAttributedString *result = [NSMutableAttributedString new];
for (NSString *key in [dict allKeys]) {
    [[result mutableString] appendFormat:@"%@=", dataKey];
    NSRange tempRange = NSMakeRange([result length] - [key length], [key length]);
    [result addAttributes:defaultKeyAttributes range:tempRange];

    [[result mutableString] appendFormat:@"%@;", [dict objectForKey:key]];
}
4

1 に答える 1

1

私はパフォーマンステストを行っていませんが、ここではあなたが得ることができるのと同じくらい良いと思います。stringWithFormatの代わりに単純な文字列連結( endを参照)を使用すると、さらに高速化できる場合があることに注意してください。元の例に近づけるために、それらを保持しました。

それはあなたのコードのかなりの作り直しを表しています

  1. ブロックを使用して各キーのディクショナリルックアップを回避し、ディクショナリが1回のヒットでキーと値の両方を返すために最適なことを実行できるようにします
  2. 事前に割り当てられたバッファ(推定)を使用して文字列を構築し、後ですべての属性を一度に追加します
  3. 追加するのではなく、特定の範囲の初期設定として使用するのsetAttributesではなく、使用する(より効率的な場合があります)。addAttributes

スニペットと同じように変換を行う関数:

NSMutableAttributedString* buildAttributedStringFromDict(NSDictionary* dict, NSDictionary* defaultKeyAttributes)
{
  NSUInteger numPairs = [dict count];
  NSUInteger guessInitialCapacity = numPairs * 20;  // use a multiplier based on your domain to avoid string expansions
  NSMutableString* builder = [NSMutableString stringWithCapacity:guessInitialCapacity];
  NSRange ranges[numPairs];
  NSRange* rp = ranges;  // use pointer to refer to C array because compiler won't let us put __block on it
  __block NSUInteger rangeIndex = 0;
  // loop building one big string and noting ranges, using optimal iteration letting dictionary feed us matching key and value
  [dict enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL* stop){
    NSString* formattedKey = [NSString stringWithFormat:@"%@=", key];
    [builder appendString:formattedKey];
    NSUInteger keylen = [formattedKey length];  // allows for anything that happens in the format
    rp[rangeIndex].length = keylen;
    rp[rangeIndex].location = [builder length] - keylen;
    ++rangeIndex;
    [builder appendFormat:@"%@;", obj];
  }];

  // convert built string into one we can add attributes to
  NSMutableAttributedString *result = [[NSMutableAttributedString alloc] initWithString:builder];
  // loop adding attributes for the key areas;
  for(rangeIndex = 0; rangeIndex < numPairs; ++rangeIndex)
  {
    [result setAttributes:defaultKeyAttributes range:ranges[rangeIndex]];
  }
  return result;
}

例として辞書リテラルを使用してコードを呼び出す:

id test = buildAttributedStringFromDict(
  @{  @"Name":@"Andy",
      @"Role":@"Boss"},
  @{  NSForegroundColorAttributeName:[UIColor redColor],
      NSFontAttributeName:[UIFont fontWithName:@"Papyrus" size:14.0]}
);

デバッグコンソール出力:

Printing description of test:
Name={
    NSColor = "UIDeviceRGBColorSpace 1 0 0 1";
    NSFont = "<UICFFont: 0x767ed50> font-family: \"Papyrus\"; font-weight: normal; font-style: normal; font-size: 14px";
}Andy;{
}Role={
    NSColor = "UIDeviceRGBColorSpace 1 0 0 1";
    NSFont = "<UICFFont: 0x767ed50> font-family: \"Papyrus\"; font-weight: normal; font-style: normal; font-size: 14px";
}Boss;{
}

更新-これは単純な連結を使用して書き直された関数です

NSMutableAttributedString* buildAttributedStringFromDict(NSDictionary* dict, NSDictionary* defaultKeyAttributes)
{
  NSUInteger numPairs = [dict count];
  NSUInteger guessInitialCapacity = numPairs * 20;  // use a multiplier based on your domain to avoid string expansions
  NSMutableString* builder = [NSMutableString stringWithCapacity:guessInitialCapacity];
  NSRange ranges[numPairs];
  NSRange* rp = ranges;  // use pointer to refer to C array because compiler won't let us put __block on it
  __block NSUInteger rangeIndex = 0;
  // loop building one big string and noting ranges, using optimal iteration letting dictionary feed us matching key and value
  [dict enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL* stop){
    [builder appendString:key];
    [builder appendString:@"="];
    NSUInteger keylen = [key length] + 1; // add 1 for = sign
    rp[rangeIndex].length = keylen;
    rp[rangeIndex].location = [builder length] - keylen;
    ++rangeIndex;
    [builder appendString:obj];
    [builder appendString:@";"];
  }];

  // convert built string into one we can add attributes to
  NSMutableAttributedString *result = [[NSMutableAttributedString alloc] initWithString:builder];
  // loop adding attributes for the key areas;
  for(rangeIndex = 0; rangeIndex < numPairs; ++rangeIndex)
  {
    [result setAttributes:defaultKeyAttributes range:ranges[rangeIndex]];
  }
  return result;
}
于 2013-09-11T07:40:46.363 に答える