NSEnumerator 性能与 Cocoa 中的 for 循环
-
09-06-2019 - |
题
我知道,如果您有一个修改循环中项目计数的循环,则在集合上使用 NSEnumerator 是确保代码崩溃的最佳方法,但是我想了解 NSEnumerator 类之间的性能权衡只是一个老派的 for 循环
解决方案
使用新的 for (... in ...)
Objective-C 2.0 中的语法通常是迭代集合的最快方法,因为它可以在堆栈上维护一个缓冲区并将批量项目放入其中。
使用 NSEnumerator
通常是最慢的方式,因为它经常复制正在迭代的集合;对于不可变集合,这可能很便宜(相当于 -retain
)但对于可变集合,它可能会导致创建不可变副本。
进行您自己的迭代 - 例如,使用 -[NSArray objectAtIndex:]
- 通常会介于两者之间,因为虽然您不会有潜在的复制开销,但您也不会从底层集合中获取批量对象。
(PS - 这个问题应该标记为 Objective-C,而不是 C,因为 NSEnumerator
是一个 Cocoa 类,新的 for (... in ...)
语法特定于 Objective-C。)
其他提示
运行测试几次后,结果几乎是一样的。每个测量块连续运行 10 次。
在我的例子中,结果从最快到最慢:
- 为..在 (测试性能示例3) (0.006 秒)
- 尽管 (测试性能示例4) (0.026 秒)
- 为了(;;) (测试性能示例1) (0.027 秒)
- 枚举块 (测试性能示例2) (0.067 秒)
for 和 while 循环几乎相同。
这 tmp
是一个 NSArray
其中包含 100 万个从 0 到 999999 的对象。
- (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
欲了解更多信息,请访问: 苹果“关于使用 Xcode 进行测试”
他们非常相似。对于 Objective-C 2.0,大多数枚举现在默认为 NSFastEnumeration
它创建一个缓冲区,其中包含集合中每个对象的地址,然后可以传递该缓冲区。与经典的 for 循环相比,您节省的一步是不必调用 objectAtIndex:i
每次在循环内。您正在枚举的集合的内部实现快速枚举,无需调用 objectAtIndex:i method
.
缓冲区是您在枚举时无法改变集合的部分原因,对象的地址将更改并且构建的缓冲区将不再匹配。
作为奖励,2.0 中的格式看起来和经典的 for 循环一样漂亮:
for ( Type newVariable in expression ) {
stmts
}
阅读以下文档以深入了解:NSFastEnumeration 协议参考