Я знаю, что если у вас есть цикл, который изменяет количество элементов в цикле, использование NSEnumerator в наборе - лучший способ убедиться, что ваш код взорвется, однако я хотел бы понять компромиссы производительности между классом NSEnumerator и просто старая школа для цикла





Они очень похожи. В Objective-C 2.0 большинство перечислений теперь по умолчанию использует NSFastEnumeration, который создает буфер адресов для каждого объекта в коллекции, который затем может доставить. Единственный шаг, который вы экономите по сравнению с классическим циклом for, - это не вызывать objectAtIndex:i каждый раз внутри цикла. Внутренняя часть перечисляемой вами коллекции реализует быстрое перечисление без вызова objectAtIndex:i method.
Буфер является одной из причин того, что вы не можете изменить коллекцию при перечислении, адрес объектов изменится, а созданный буфер больше не будет соответствовать.
В качестве бонуса формат в 2.0 выглядит так же красиво, как и классический цикл for:
for ( Type newVariable in expression ) {
stmts
}
Прочтите следующую документацию, чтобы узнать больше: Справочник по протоколу NSFastEnumeration
@matt в будущем нажимаем «редактировать» и улучшаем пост для наших попутчиков. Я не занимаюсь разработкой для iOS .. :)
Использование нового синтаксиса for (... in ...) в Objective-C 2.0, как правило, является самым быстрым способом перебора коллекции, поскольку он может поддерживать буфер в стеке и загружать в него партии элементов.
Использование NSEnumerator, как правило, является самым медленным способом, поскольку он часто копирует повторяемую коллекцию; для неизменяемых коллекций это может быть дешево (эквивалентно -retain), но для изменяемых коллекций это может привести к созданию неизменяемой копии.
Выполнение собственной итерации - например, с использованием -[NSArray objectAtIndex:] - обычно находится где-то посередине, потому что, хотя у вас не будет потенциальных накладных расходов на копирование, вы также не получите пакеты объектов из базовой коллекции.
(PS - этот вопрос должен быть помечен как Objective-C, а не C, поскольку NSEnumerator является классом Какао, а новый синтаксис for (... in ...) специфичен для Objective-C.)
После нескольких прогонов теста результат практически одинаковый. Каждый блок измерения выполняется последовательно 10 раз.
Результат в моем случае от самого быстрого до самого медленного:
Циклы 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
Для получения дополнительной информации посетите: Яблоки "О тестировании с Xcode"
Ваш вывод правильный, но тест за(;;) может быть быстрее, если не вызывать [tmp count] после каждого цикла.
Да, вы правы, в этом случае результат теста за(;;) - 0,023, а результат теста цикла пока - 0,022
@ Джефф Этвуд, эта ссылка у меня не сработала. Вместо этого я только что установил Xcode 4. Я поискал
NSFastEnumerationв документации Xcode и справочнике по API, чтобы найти справочник по протоколу NSFastEnumeration.