我知道,如果您有一个修改循环中项目计数的循环,则在集合上使用 NSEnumerator 是确保代码崩溃的最佳方法,但是我想了解 NSEnumerator 类之间的性能权衡只是一个老派的循环
3 回答
使用 Objective-C 2.0 中的新for (... in ...)
语法通常是迭代集合的最快方法,因为它可以在堆栈上维护一个缓冲区并将成批的项目放入其中。
使用NSEnumerator
通常是最慢的方式,因为它经常复制正在迭代的集合;对于不可变集合,这可能很便宜(相当于-retain
),但对于可变集合,它可能会导致创建不可变副本。
进行自己的迭代(例如,使用-[NSArray objectAtIndex:]
)通常介于两者之间,因为虽然您不会有潜在的复制开销,但您也不会从底层集合中获取成批的对象。
(PS - 这个问题应该标记为 Objective-C,而不是 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;
}
整个代码:
视图控制器.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 进行测试”
它们非常相似。在 Objective-C 2.0 中,大多数枚举现在默认使用NSFastEnumeration
它为集合中的每个对象创建一个地址缓冲区,然后它可以传递。您在经典 for 循环中节省的一步是不必objectAtIndex:i
每次都在循环内调用。您正在枚举的集合的内部实现了无需调用即可快速枚举objectAtIndex:i method
。
缓冲区是您无法在枚举时更改集合的部分原因,对象的地址将更改并且构建的缓冲区将不再匹配。
作为奖励,2.0 中的格式看起来和经典的 for 循环一样好:
for ( Type newVariable in expression ) {
stmts
}
阅读以下文档以更深入地了解: NSFastEnumeration 协议参考