Pergunta

Eu sei que se você tem um loop que modifica a contagem dos itens no loop, usar o NSEnumerator em um set é a melhor maneira de garantir que seu código exploda, porém eu gostaria de entender as compensações de desempenho entre a classe NSEnumerator e apenas um loop for old school

Foi útil?

Solução

Usando o novo for (... in ...) A sintaxe no Objective-C 2.0 é geralmente a maneira mais rápida de iterar em uma coleção porque pode manter um buffer na pilha e colocar lotes de itens nele.

Usando NSEnumerator geralmente é a maneira mais lenta porque geralmente copia a coleção que está sendo iterada;para coleções imutáveis, isso pode ser barato (equivalente a -retain), mas para coleções mutáveis ​​pode causar a criação de uma cópia imutável.

Fazendo sua própria iteração — por exemplo, usando -[NSArray objectAtIndex:] - geralmente ficará em algum ponto intermediário porque, embora você não tenha a sobrecarga potencial de cópia, também não obterá lotes de objetos da coleção subjacente.

(PS - Esta questão deve ser marcada como Objective-C, não C, já que NSEnumerator é uma classe Cocoa e a nova for (... in ...) a sintaxe é específica do Objective-C.)

Outras dicas

Depois de executar o teste várias vezes, o resultado é quase o mesmo.Cada bloco de medida é executado 10 vezes consecutivas.

O resultado no meu caso, do mais rápido para o mais lento:

  1. Para..em (testePerformanceExample3) (0,006 seg.)
  2. Enquanto (testePerformanceExample4) (0,026 seg.)
  3. Para(;;) (testePerformanceExample1) (0,027 seg)
  4. Bloco de enumeração (testePerformanceExample2) (0,067 seg)

O loop for e while é quase o mesmo.

comparation between iterations

O tmp é um NSArray que contém 1 milhão de objetos de 0 a 999999.

- (NSArray *)createArray
{
    self.tmpArray = [NSMutableArray array];
    for (int i = 0; i < 1000000; i++)
    {
        [self.tmpArray addObject:@(i)];
    }
    return self.tmpArray;
}

Todo o código:

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

MeuArquivoTeste.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

Para mais informações visite: Maçãs "Sobre testes com Xcode"

Eles são muito parecidos.Com o Objective-C 2.0, a maioria das enumerações agora é padronizada NSFastEnumeration que cria um buffer de endereços para cada objeto da coleção que ele pode entregar.A única etapa que você economiza no loop for clássico é não precisar chamar objectAtIndex:i cada vez dentro do loop.Os componentes internos da coleção que você está enumerando implementam enumeração rápida sem chamar objectAtIndex:i method.

O buffer é parte do motivo pelo qual você não pode alterar uma coleção ao enumerar, o endereço dos objetos será alterado e o buffer que foi construído não corresponderá mais.

Como bônus, o formato 2.0 parece tão bom quanto o clássico for loop:

for ( Type newVariable in expression ) { 
    stmts 
}

Leia a seguinte documentação para se aprofundar:Referência do protocolo NSFastEnumeration

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top