ループ内のアイテムの数を変更するループがある場合、セットで NSEnumerator を使用することがコードを爆発させる最善の方法であることはわかっていますが、NSEnumerator クラス間のパフォーマンスのトレードオフを理解したいと思います。ループのための古い学校
3 に答える
Objective-C 2.0の新しいfor (... in ...)
構文を使用すると、通常、コレクションを反復処理する最速の方法になります。これは、スタック上のバッファーを維持し、項目のバッチをそこに取得できるためです。
NSEnumerator
反復されるコレクションをコピーすることが多いため、使用は一般的に最も遅い方法です。不変コレクションの場合、これは安価な場合があります ( と同等-retain
) が、可変コレクションの場合、不変コピーが作成される可能性があります。
独自の反復を行う場合 (たとえば、を使用する場合)は、-[NSArray objectAtIndex:]
通常、コピーのオーバーヘッドが発生する可能性はありませんが、基になるコレクションからオブジェクトのバッチを取得することもないため、その中間に位置します。
(PS - この質問は、C ではなく Objective-C としてタグ付けする必要があります。これNSEnumerator
は、Cocoa クラスであり、新しいfor (... in ...)
構文が Objective-C に固有であるためです。)
テストを数回実行した後、結果はほぼ同じです。各測定ブロックは 10 回連続して実行されます。
私の場合、最速から最低までの結果:
- For..in (testPerformanceExample3) (0.006 秒)
- While (testPerformanceExample4) (0.026 秒)
- For(;;) (testPerformanceExample1) (0.027 秒)
- 列挙ブロック(testPerformanceExample2) (0.067 秒)
for ループと while ループはほとんど同じです。
はtmp
、NSArray
0 から 999999 までの 100 万個のオブジェクトを含む です。
- (NSArray *)createArray
{
self.tmpArray = [NSMutableArray array];
for (int i = 0; i < 1000000; i++)
{
[self.tmpArray addObject:@(i)];
}
return self.tmpArray;
}
コード全体:
ViewController.h
#import <UIKit/UIKit.h>
@interface ViewController : UIViewController
@property (strong, nonatomic) NSMutableArray *tmpArray;
- (NSArray *)createArray;
@end
ViewController.m
#import "ViewController.h"
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self createArray];
}
- (NSArray *)createArray
{
self.tmpArray = [NSMutableArray array];
for (int i = 0; i < 1000000; i++)
{
[self.tmpArray addObject:@(i)];
}
return self.tmpArray;
}
@end
MyTestfile.m
#import <UIKit/UIKit.h>
#import <XCTest/XCTest.h>
#import "ViewController.h"
@interface TestCaseXcodeTests : XCTestCase
{
ViewController *vc;
NSArray *tmp;
}
@end
@implementation TestCaseXcodeTests
- (void)setUp {
[super setUp];
vc = [[ViewController alloc] init];
tmp = vc.createArray;
}
- (void)testPerformanceExample1
{
[self measureBlock:^{
for (int i = 0; i < [tmp count]; i++)
{
[tmp objectAtIndex:i];
}
}];
}
- (void)testPerformanceExample2
{
[self measureBlock:^{
[tmp enumerateObjectsUsingBlock:^(NSNumber *obj, NSUInteger idx, BOOL *stop) {
obj;
}];
}];
}
- (void)testPerformanceExample3
{
[self measureBlock:^{
for (NSNumber *num in tmp)
{
num;
}
}];
}
- (void)testPerformanceExample4
{
[self measureBlock:^{
int i = 0;
while (i < [tmp count])
{
[tmp objectAtIndex:i];
i++;
}
}];
}
@end
それらは非常に似ています。Objective-C 2.0 では、ほとんどの列挙がデフォルトNSFastEnumeration
で、コレクション内の各オブジェクトへのアドレスのバッファを作成し、配信できるようになりました。objectAtIndex:i
従来の for ループよりも節約できる 1 つのステップは、ループ内で毎回呼び出す必要がないことです。列挙しているコレクションの内部は、呼び出しなしで高速な列挙を実装しますobjectAtIndex:i method
。
バッファーは、列挙時にコレクションを変更できない理由の一部です。オブジェクトのアドレスが変更され、構築されたバッファーが一致しなくなります。
おまけとして、2.0 の形式は従来の for ループと同じくらい見栄えがします。
for ( Type newVariable in expression ) {
stmts
}
詳細については、次のドキュメントをお読みください: NSFastEnumeration プロトコル リファレンス