代码之家  ›  专栏  ›  技术社区  ›  Heat Miser

Cocoa中NSEnumerator性能与for循环的比较

  •  19
  • Heat Miser  · 技术社区  · 17 年前

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

    3 回复  |  直到 17 年前
        1
  •  27
  •   Chris Hanson    17 年前

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

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

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

    (PS-这个问题应该标记为Objective-C,而不是C,因为 NS枚举器 是一个Cocoa类和新 对于(…in…) 语法是Objective-C特有的。)

        2
  •  5
  •   Zsivics Sanel    11 年前

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

    在我的例子中,结果从最快到最慢:

    1. 为了。.in (测试性能示例3) (0.006秒)
    2. 与…同时 (测试性能示例4) (0.026秒)
    3. 对于(;;) (测试性能示例1) (0.027秒)
    4. 枚举块 (测试性能示例2) (0.067秒)

    for和while循环几乎相同。

    comparation between iterations

    这个 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。英语字母表的第8个字母

    #import <UIKit/UIKit.h>
    
    @interface ViewController : UIViewController
    
    @property (strong, nonatomic) NSMutableArray *tmpArray;
    - (NSArray *)createArray;
    
    @end
    

    ViewController。男性

    #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。男性

    #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 "About Testing with Xcode"

        3
  •  2
  •   Alexander Kradenkov    11 年前

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

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

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

    for ( Type newVariable in expression ) { 
        stmts 
    }
    

    阅读以下文档以深入了解: NSFastEnumeration Protocol Reference

    推荐文章