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

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

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
18
0
6 788
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

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

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

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

for ( Type newVariable in expression ) { 
    stmts 
}

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

@ Джефф Этвуд, эта ссылка у меня не сработала. Вместо этого я только что установил Xcode 4. Я поискал NSFastEnumeration в документации Xcode и справочнике по API, чтобы найти справочник по протоколу NSFastEnumeration.

ma11hew28 17.04.2011 07:22

@matt в будущем нажимаем «редактировать» и улучшаем пост для наших попутчиков. Я не занимаюсь разработкой для iOS .. :)

Jeff Atwood 17.04.2011 07:32
Ответ принят как подходящий

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

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

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

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

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

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

  1. Для..в (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"

Ваш вывод правильный, но тест за(;;) может быть быстрее, если не вызывать [tmp count] после каждого цикла.

Jesse Gumpo 28.01.2017 07:14

Да, вы правы, в этом случае результат теста за(;;) - 0,023, а результат теста цикла пока - 0,022

Zsivics Sanel 28.01.2017 20:06

Другие вопросы по теме