Производительность NSEnumerator по сравнению с циклом for в Cocoa
-
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 раз подряд.
Результат в моем случае от самого быстрого к самому медленному:
- For..in (Testperformance example3) (0,006 секунды)
- В то время как (Testperformance example4) (0,026 секунды)
- Для (;;) (Testperformance example1) (0,027 секунды)
- Блок перечисления (Testperformance example2) (0,067 секунды)
Цикл for и while практически одинаков.
Тот Самый tmp
является NSArray
который содержит 1 миллион объектов от 0 до 999999.
- (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
Для получения дополнительной информации посетите сайт: Apple "О тестировании с помощью Xcode"
Они очень похожи.В Objective-C 2.0 большинство перечислений теперь имеют значение по умолчанию NSFastEnumeration
который создает буфер адресов для каждого объекта в коллекции, который он затем может доставить.Единственный шаг, который вы сохраняете по сравнению с классическим циклом for, - это отсутствие необходимости вызывать objectAtIndex:i
каждый раз внутри цикла.Внутренние компоненты коллекции, которую вы перечисляете, реализуют быстрое перечисление без вызова out objectAtIndex:i method
.
Буфер является одной из причин того, что вы не можете изменять коллекцию при перечислении, адрес объектов изменится, и созданный буфер больше не будет соответствовать.
В качестве бонуса формат в версии 2.0 выглядит так же красиво, как и классический цикл for:
for ( Type newVariable in expression ) {
stmts
}
Прочтите следующий документ, чтобы углубиться в него:Ссылка на протокол NSFastEnumeration