19

我知道,如果您有一个修改循环中项目计数的循环,则在集合上使用 NSEnumerator 是确保代码崩溃的最佳方法,但是我想了解 NSEnumerator 类之间的性能权衡只是一个老派的循环

4

3 回答 3

27

使用 Objective-C 2.0 中的新for (... in ...)语法通常是迭代集合的最快方法,因为它可以在堆栈上维护一个缓冲区并将成批的项目放入其中。

使用NSEnumerator通常是最慢的方式,因为它经常复制正在迭代的集合;对于不可变集合,这可能很便宜(相当于-retain),但对于可变集合,它可能会导致创建不可变副本。

进行自己的迭代(例如,使用-[NSArray objectAtIndex:])通常介于两者之间,因为虽然您不会有潜在的复制开销,但您也不会从底层集合中获取成批的对象。

(PS - 这个问题应该标记为 Objective-C,而不是 C,因为NSEnumerator它是一个 Cocoa 类并且新for (... in ...)语法是特定于 Objective-C 的。)

于 2008-08-28T19:59:48.500 回答
5

多次运行测试后,结果几乎相同。每个测量块连续运行 10 次。

在我的情况下,结果从最快到最慢:

  1. For..in (testPerformanceExample3) (0.006 秒)
  2. While (testPerformanceExample4) (0.026 秒)
  3. For(;;) (testPerformanceExample1) (0.027 秒)
  4. 枚举块(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;
}

整个代码:

视图控制器.h

#import <UIKit/UIKit.h>

@interface ViewController : UIViewController

@property (strong, nonatomic) NSMutableArray *tmpArray;
- (NSArray *)createArray;

@end

视图控制器.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

我的测试文件.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

欲了解更多信息,请访问:Apples“关于使用 Xcode 进行测试”

于 2015-06-08T22:47:16.380 回答
2

它们非常相似。在 Objective-C 2.0 中,大多数枚举现在默认使用NSFastEnumeration它为集合中的每个对象创建一个地址缓冲区,然后它可以传递。您在经典 for 循环中节省的一步是不必objectAtIndex:i每次都在循环内调用。您正在枚举的集合的内部实现了无需调用即可快速枚举objectAtIndex:i method

缓冲区是您无法在枚举时更改集合的部分原因,对象的地址将更改并且构建的缓冲区将不再匹配。

作为奖励,2.0 中的格式看起来和经典的 for 循环一样好:

for ( Type newVariable in expression ) { 
    stmts 
}

阅读以下文档以更深入地了解: NSFastEnumeration 协议参考

于 2008-08-28T18:37:27.543 回答