Производительность NSEnumerator против цикла в Какао - PullRequest
17 голосов
/ 28 августа 2008

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

Ответы [ 3 ]

27 голосов
/ 28 августа 2008

Использование нового синтаксиса for (... in ...) в Objective-C 2.0, как правило, является самым быстрым способом перебора коллекции, поскольку он может поддерживать буфер в стеке и получать в него партии элементов.

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

Выполнение вашей собственной итерации - например, использование -[NSArray objectAtIndex:] - обычно будет где-то посередине, потому что, хотя у вас не будет потенциальных затрат на копирование, вы также не будете получать пакеты объектов из базовой коллекции.

(PS - этот вопрос должен быть помечен как Objective-C, а не C, поскольку NSEnumerator является классом Какао, а новый синтаксис for (... in ...) специфичен для Objective-C.)

5 голосов
/ 09 июня 2015

После запуска теста несколько раз, результат почти тот же. Каждый блок измерений выполняется 10 раз подряд.

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

  1. For..in (testPerformanceExample3) (0,006 с)
  2. Пока (testPerformanceExample4) (0,026 с)
  3. Для (;;) (testPerformanceExample1) (0,027 с)
  4. Блок перечисления (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"

2 голосов
/ 28 августа 2008

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

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

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

for ( Type newVariable in expression ) { 
    stmts 
}

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

...