私はパフォーマンステストを行っていませんが、ここではあなたが得ることができるのと同じくらい良いと思います。stringWithFormatの代わりに単純な文字列連結( endを参照)を使用すると、さらに高速化できる場合があることに注意してください。元の例に近づけるために、それらを保持しました。
それはあなたのコードのかなりの作り直しを表しています
- ブロックを使用して各キーのディクショナリルックアップを回避し、ディクショナリが1回のヒットでキーと値の両方を返すために最適なことを実行できるようにします
- 事前に割り当てられたバッファ(推定)を使用して文字列を構築し、後ですべての属性を一度に追加します
- 追加するのではなく、特定の範囲の初期設定として使用するの
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;
}