Производительность NSEnumerator по сравнению с циклом for в Cocoa

StackOverflow https://stackoverflow.com/questions/32986

  •  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 раз подряд.

Результат в моем случае от самого быстрого к самому медленному:

  1. For..in (Testperformance example3) (0,006 секунды)
  2. В то время как (Testperformance example4) (0,026 секунды)
  3. Для (;;) (Testperformance example1) (0,027 секунды)
  4. Блок перечисления (Testperformance example2) (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

Для получения дополнительной информации посетите сайт: Apple "О тестировании с помощью Xcode"

Они очень похожи.В Objective-C 2.0 большинство перечислений теперь имеют значение по умолчанию NSFastEnumeration который создает буфер адресов для каждого объекта в коллекции, который он затем может доставить.Единственный шаг, который вы сохраняете по сравнению с классическим циклом for, - это отсутствие необходимости вызывать objectAtIndex:i каждый раз внутри цикла.Внутренние компоненты коллекции, которую вы перечисляете, реализуют быстрое перечисление без вызова out objectAtIndex:i method.

Буфер является одной из причин того, что вы не можете изменять коллекцию при перечислении, адрес объектов изменится, и созданный буфер больше не будет соответствовать.

В качестве бонуса формат в версии 2.0 выглядит так же красиво, как и классический цикл for:

for ( Type newVariable in expression ) { 
    stmts 
}

Прочтите следующий документ, чтобы углубиться в него:Ссылка на протокол NSFastEnumeration

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top