Pergunta

Eu estou procurando o idioma padrão para iterar um NSArray. Minhas necessidades de código para ser adequado para OS X 10.4 +.

Foi útil?

Solução

O código geralmente preferida para 10,5 + / IOS.

for (id object in array) {
    // do something with object
}

Esta construção é utilizada para objectos enumerar numa recolha que está em conformidade com o protocolo NSFastEnumeration . Esta abordagem tem uma vantagem de velocidade porque ele armazena apontadores para vários objectos (obtidas por meio de uma chamada de método único) em um tampão e itera-los através do avanço através do tampão usando aritmética de ponteiro. Esta é muito mais rápido do que chamar -objectAtIndex: cada vez através do loop.

Também é importante notar que, enquanto você tecnicamente pode usar um para-in loop para etapa através de um NSEnumerator, eu descobri que este anula praticamente toda a vantagem da velocidade de contagem rápida. A razão é que a implementação NSEnumerator padrão de lugares -countByEnumeratingWithState:objects:count: apenas um objeto no buffer em cada chamada.

Eu relatei em radar://6296108 (enumeração rápida de NSEnumerators é lento), mas ele foi devolvido a não ser corrigido. A razão é que a enumeração rápida pré-busca um grupo de objetos, e se você quiser para enumerar apenas para um determinado ponto no recenseador (por exemplo, até que um determinado objeto é encontrado, ou condição for atendida) e usar o mesmo recenseador após sair do loop, muitas vezes seria o caso de que vários objetos seriam ignorados.

Se você está codificando para OS X 10.6 / iOS 4.0 e acima, você também tem a opção de usar APIs baseados em blocos para enumerar matrizes e outras coleções:

[array enumerateObjectsUsingBlock:^(id object, NSUInteger idx, BOOL *stop) {
    // do something with object
}];

Você também pode usar -enumerateObjectsWithOptions:usingBlock: e passar NSEnumerationConcurrent e / ou NSEnumerationReverse como o argumento opções.


10.4 ou anterior

A expressão padrão de pré-10.5 é a utilização de um NSEnumerator e um tempo de loop, assim:

NSEnumerator *e = [array objectEnumerator];
id object;
while (object = [e nextObject]) {
  // do something with object
}

Eu recomendo manter as coisas simples. Amarrando-se a um tipo de matriz é inflexível, e o aumento da velocidade suposto de usar -objectAtIndex: é insignificante para a melhoria com a enumeração rápida em 10.5+ de qualquer maneira. (Enumeração rápida realmente usa ponteiro aritmética sobre a estrutura de dados subjacente, e remove a maior parte da sobrecarga chamada de método.) Otimização prematura nunca é uma boa ideia - que resulta em código mais confusa para resolver um problema que não é o gargalo de qualquer maneira

Ao usar o -objectEnumerator , você muito facilmente mudar para outra coleção enumerável (como um NSSet, chaves em um NSDictionary, etc.), ou switch até mesmo para -reverseObjectEnumerator para enumerar uma matriz para trás, todos sem outras alterações de código. Se o código de iteração está em um método, você poderia até mesmo passar em qualquer NSEnumerator eo código não tem sequer se preocupar com o é a iteração. Além disso, um NSEnumerator (pelo menos as previstas pelo código Apple) mantém a coleção é enumerar enquanto existem mais objetos, assim você não precisa se preocupar com quanto tempo um objeto autoreleased vai existir.

Talvez a maior coisa que um NSEnumerator (ou contagem rápida) protege você de se ter uma coleção mutável (array ou não) a mudança debaixo de você sem o seu conhecimento enquanto você está enumerando-lo. Se você acessar os objetos por índice, você pode executar em exceções estranhas ou off-by-one erros (muitas vezes muito tempo depois o problema ocorreu) que pode ser terrível para depurar. Enumeração usando um dos idiomas padrão tem um "fail-fast" comportamento, então o problema (provocado por código incorreto) irá manifestar-se imediatamente quando você tentar acessar o próximo objeto depois que a mutação ocorreu. Como os programas se tornam mais complexas e multi-threaded, ou até mesmo depender de algo que código de terceiros pode modificar, código de enumeração frágil torna-se cada vez mais problemático. Encapsulamento e abstração FTW! : -)


Outras dicas

Para OS X 10.4.x e anterior:

 int i;
 for (i = 0; i < [myArray count]; i++) {
   id myArrayElement = [myArray objectAtIndex:i];
   ...do something useful with myArrayElement
 }

Para OS X 10.5.x (ou iPhone) e além:

for (id myArrayElement in myArray) {
   ...do something useful with myArrayElement
}

Os resultados do código de teste e fonte estão abaixo (você pode definir o número de iterações no app). O tempo é em milissegundos, e cada entrada é um resultado médio de executar o teste de 5-10 vezes. Descobri que geralmente é preciso para 2-3 dígitos significativos e depois que ele iria variar de acordo com cada execução. Isso dá uma margem de erro inferior a 1%. O teste foi executado em um iPhone 3G, que é a plataforma de destino eu estava interessado.

numberOfItems   NSArray (ms)    C Array (ms)    Ratio
100             0.39            0.0025          156
191             0.61            0.0028          218
3,256           12.5            0.026           481
4,789           16              0.037           432
6,794           21              0.050           420
10,919          36              0.081           444
19,731          64              0.15            427
22,030          75              0.162           463
32,758          109             0.24            454
77,969          258             0.57            453
100,000         390             0.73            534

As classes fornecidas pelo cacau para a manipulação de conjuntos de dados (NSDictionary, NSArray, NSSet etc.) fornecem uma interface muito agradável para o gerenciamento de informações, sem ter que se preocupar com a burocracia do gerenciamento de memória, realocação etc. Claro que isso vem na um custo embora. Eu acho que é bastante óbvio que dizem usar um NSArray de NSNumbers vai ser mais lento do que uma matriz C de carros alegóricos para iterações simples, então eu decidi fazer alguns testes, e os resultados foram muito chocante! Eu não estava esperando que fosse tão ruim. Nota:. Esses testes são realizados em um iPhone 3G, que é a plataforma de destino eu estava interessado em

Neste teste eu faço uma comparação muito simples desempenho de acesso aleatório entre uma bóia C * e NSArray de NSNumbers

Eu criar um loop simples para resumir o conteúdo de cada array e tempo-los usando mach_absolute_time (). O NSMutableArray leva em média 400 vezes mais !! (Não 400 por cento, apenas a 400 vezes mais! Isso é 40.000% maior!).

Cabeçalho:

// Array_Speed_TestViewController.h

// matriz Speed ??Test

// Criado por Mehmet Akten em 05/02/2009.

// Direitos de autor MSA Visuals Ltd. 2009. Todos os direitos reservados.

#import <UIKit/UIKit.h>

@interface Array_Speed_TestViewController : UIViewController {

    int                     numberOfItems;          // number of items in array

    float                   *cArray;                // normal c array

    NSMutableArray          *nsArray;               // ns array

    double                  machTimerMillisMult;    // multiplier to convert mach_absolute_time() to milliseconds



    IBOutlet    UISlider    *sliderCount;

    IBOutlet    UILabel     *labelCount;


    IBOutlet    UILabel     *labelResults;

}


-(IBAction) doNSArray:(id)sender;

-(IBAction) doCArray:(id)sender;

-(IBAction) sliderChanged:(id)sender;


@end

Implementação:

// Array_Speed_TestViewController.m

// matriz Speed ??Test

// Criado por Mehmet Akten em 05/02/2009.

// Direitos de autor MSA Visuals Ltd. 2009. Todos os direitos reservados.

    #import "Array_Speed_TestViewController.h"
    #include <mach/mach.h>
    #include <mach/mach_time.h>

 @implementation Array_Speed_TestViewController



 // Implement viewDidLoad to do additional setup after loading the view, typically from a nib.

- (void)viewDidLoad {

    NSLog(@"viewDidLoad");


    [super viewDidLoad];


    cArray      = NULL;

    nsArray     = NULL;


    // read initial slider value setup accordingly

    [self sliderChanged:sliderCount];


    // get mach timer unit size and calculater millisecond factor

    mach_timebase_info_data_t info;

    mach_timebase_info(&info);

    machTimerMillisMult = (double)info.numer / ((double)info.denom * 1000000.0);

    NSLog(@"machTimerMillisMult = %f", machTimerMillisMult);

}



// pass in results of mach_absolute_time()

// this converts to milliseconds and outputs to the label

-(void)displayResult:(uint64_t)duration {

    double millis = duration * machTimerMillisMult;


    NSLog(@"displayResult: %f milliseconds", millis);


    NSString *str = [[NSString alloc] initWithFormat:@"%f milliseconds", millis];

    [labelResults setText:str];

    [str release];

}




// process using NSArray

-(IBAction) doNSArray:(id)sender {

    NSLog(@"doNSArray: %@", sender);


    uint64_t startTime = mach_absolute_time();

    float total = 0;

    for(int i=0; i<numberOfItems; i++) {

        total += [[nsArray objectAtIndex:i] floatValue];

    }

    [self displayResult:mach_absolute_time() - startTime];

}




// process using C Array

-(IBAction) doCArray:(id)sender {

    NSLog(@"doCArray: %@", sender);


    uint64_t start = mach_absolute_time();

    float total = 0;

    for(int i=0; i<numberOfItems; i++) {

        total += cArray[i];

    }

    [self displayResult:mach_absolute_time() - start];

}



// allocate NSArray and C Array 

-(void) allocateArrays {

    NSLog(@"allocateArrays");


    // allocate c array

    if(cArray) delete cArray;

    cArray = new float[numberOfItems];


    // allocate NSArray

    [nsArray release];

    nsArray = [[NSMutableArray alloc] initWithCapacity:numberOfItems];



    // fill with random values

    for(int i=0; i<numberOfItems; i++) {

        // add number to c array

        cArray[i] = random() * 1.0f/(RAND_MAX+1);


        // add number to NSArray

        NSNumber *number = [[NSNumber alloc] initWithFloat:cArray[i]];

        [nsArray addObject:number];

        [number release];

    }


}



// callback for when slider is changed

-(IBAction) sliderChanged:(id)sender {

    numberOfItems = sliderCount.value;

    NSLog(@"sliderChanged: %@, %i", sender, numberOfItems);


    NSString *str = [[NSString alloc] initWithFormat:@"%i items", numberOfItems];

    [labelCount setText:str];

    [str release];


    [self allocateArrays];

}



//cleanup

- (void)dealloc {

    [nsArray release];

    if(cArray) delete cArray;


    [super dealloc];

}


@end

De: memo.tv

////////////////////

Disponível desde a introdução de blocos, isso permite para iterar uma matriz com blocos. Sua sintaxe não é tão agradável como enumeração rápido, mas há uma característica muito interessante: enumeração simultânea. Se a ordem de enumeração não é importante e os postos de trabalho pode ser feito em paralelo, sem bloqueio, este pode proporcionar um aumento de velocidade considerável em um sistema multi-core. Mais sobre isso na seção de enumeração simultânea.

[myArray enumerateObjectsUsingBlock:^(id object, NSUInteger index, BOOL *stop) {
    [self doSomethingWith:object];
}];
[myArray enumerateObjectsWithOptions:NSEnumerationConcurrent usingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
    [self doSomethingWith:object];
}];

/////////// NSFastEnumerator

A idéia por trás contagem rápida é usar o acesso rápido série C para iteração otimizar. Não só é suposto ser mais rápido do que o tradicional NSEnumerator, mas Objective-C 2.0 também fornece uma sintaxe muito concisa.

id object;
for (object in myArray) {
    [self doSomethingWith:object];
}

/////////////////

NSEnumerator

Esta é uma forma de iteração externo: [meuArray objectEnumerator] devolve um objecto. Este objeto tem um método nextObject que podemos chamar em um loop até que ele retorne nil

NSEnumerator *enumerator = [myArray objectEnumerator];
id object;
while (object = [enumerator nextObject]) {
    [self doSomethingWith:object];
}

/////////////////

objectAtIndex: enumeração

Utilizando um circuito para o qual aumenta um número inteiro e consultando o objecto utilizando. [MeuArray objectAtIndex: índice] é a forma mais básica de enumeração

NSUInteger count = [myArray count];
for (NSUInteger index = 0; index < count ; index++) {
    [self doSomethingWith:[myArray objectAtIndex:index]];
}

////////////// De: darkdust.net

As três formas são:

        //NSArray
    NSArray *arrData = @[@1,@2,@3,@4];

    // 1.Classical
    for (int i=0; i< [arrData count]; i++){
        NSLog(@"[%d]:%@",i,arrData[i]);
    }

    // 2.Fast iteration
    for (id element in arrData){
        NSLog(@"%@",element);
    }

    // 3.Blocks
    [arrData enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
         NSLog(@"[%lu]:%@",idx,obj);
         // Set stop to YES in case you want to break the iteration
    }];
  1. é o caminho mais rápido na execução, e 3. com autocompletar esquecer escrever iteração envelope.

Adicionar método each em sua NSArray category, você vai precisar dele um monte

código retirado ObjectiveSugar

- (void)each:(void (^)(id object))block {
    [self enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
        block(obj);
    }];
}

Aqui está como você declarar uma matriz de strings e interagir sobre eles:

NSArray *langs = @[@"es", @"en", @"pt", @"it", @"fr"];

for (int i = 0; i < [langs count]; i++) {
  NSString *lang = (NSString*) [langs objectAtIndex:i];
  NSLog(@"%@, ",lang);
}

Para Swift

let arrayNumbers = [11, 12, 13, 14, 15, 16, 17, 18, 19, 20]

// 1
for (index, value) in arrayNumbers.enumerated() {
    print(index, value)
    //... do somthing with array value and index
}


//2
for value in arrayNumbers {
    print(value)
    //... do somthing with array value
}

Faça o seguinte: -

for (id object in array) 
{
        // statement
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top