Производительность NSEnumerator против цикла в Cocoa
Я знаю, что если у вас есть цикл, который изменяет количество элементов в цикле, использование NSEnumerator в наборе - лучший способ убедиться, что ваш код взорван, однако я хотел бы понять компромисс производительности между класс NSEnumerator и только старая школа для цикла
Ответы
Ответ 1
Использование нового синтаксиса for (... in ...)
в Objective-C 2.0, как правило, является самым быстрым способом итерации по коллекции, поскольку он может поддерживать буфер в стеке и получать в него партии элементов.
Использование NSEnumerator
, как правило, является самым медленным способом, поскольку он часто копирует итерированную коллекцию; для неизменяемых коллекций это может быть дешево (эквивалентно -retain
), но для изменчивых коллекций он может создать неизменяемую копию.
Выполнение вашей собственной итерации - например, с помощью -[NSArray objectAtIndex:]
- обычно будет падать где-то посередине, потому что, пока у вас не будет потенциальных накладных расходов на копирование, вы также не будете получать партии объектов из базовой коллекции.
(PS - этот вопрос должен быть помечен как Objective-C, а не C, поскольку NSEnumerator
является классом Cocoa, а новый синтаксис for (... in ...)
специфичен для Objective-C.)
Ответ 2
После запуска теста несколько раз результат почти такой же. Каждый блок измерения выполняется 10 раз подряд.
Результат в моем случае от самого быстрого до самого медленного:
- For..in (testPerformanceExample3) (0.006 сек)
- Пока (testPerformanceExample4) (0,026 сек)
- Для (;;) (testPerformanceExample1) (0,027 сек)
- Блок перечисления (testPerformanceExample2) (0,067 сек)
Цикл for и while почти то же самое.
![comparation between iterations]()
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
Для получения дополнительной информации посетите: Яблоки "О тестировании с помощью Xcode"
Ответ 3
Они очень похожи. С Objective-C 2.0 в большинстве перечислений теперь по умолчанию используется NSFastEnumeration
, который создает буфер адресов для каждого объекта в коллекции, который он может доставить. Один шаг, который вы сохраняете по сравнению с классическим для цикла, не должен вызывать objectAtIndex:i
каждый раз внутри цикла. Внутренности коллекции, которую вы перечисляете, реализуют быстрое перечисление без вызова objectAtIndex:i method
.
Буфер является частью причины, по которой вы не можете мутировать коллекцию при перечислении, адрес объектов изменится, и созданный буфер больше не будет соответствовать.
В качестве бонуса формат в 2.0 выглядит так же хорошо, как классический для цикла:
for ( Type newVariable in expression ) {
stmts
}
Прочтите следующую документацию, чтобы углубиться:
Ссылка протокола NSFastEnumeration