Comment puis-je itérer sur un NSArray?

Je cherche l'idiome standard à itérer sur un NSArray. Mon code doit être adapté pour OS X 10.4+.

418
demandé sur Quinn Taylor 2009-06-14 18:21:10

7 réponses

Le code généralement préféré pour 10.5+ / iOS.

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

Cette construction est utilisée pour énumérer des objets dans une collection qui NSFastEnumeration protocole. Cette approche a un avantage de vitesse car elle stocke des pointeurs vers plusieurs objets (obtenus via un seul appel de méthode) dans un tampon et les parcourt en avançant dans le tampon en utilisant l'arithmétique du pointeur. C'est beaucoup plus rapide que d'appeler -objectAtIndex: chaque passage dans la boucle.

C'est aussi il est à noter que si techniquement vous pouvez utiliser une boucle for-in pour passer à travers un NSEnumerator, j'ai trouvé que cela annule pratiquement tout l'avantage de vitesse de l'énumération rapide. La raison en est que l'implémentation par défaut NSEnumerator de -countByEnumeratingWithState:objects:count: place un seul objet dans le tampon à chaque appel.

J'ai signalé cela dans radar://6296108 (l'énumération rapide des NSEnumerators est lente) mais il a été renvoyé comme Ne pas être corrigé. La raison en est que l'énumération rapide pré-récupère un groupe d'objets, et si vous voulez énumérer uniquement à un point donné dans l'énumérateur (par exemple jusqu'à ce qu'un objet particulier soit trouvé ou que la condition soit remplie) et utiliser le même énumérateur après avoir sorti de la boucle, il serait souvent le cas que plusieurs objets soient ignorés.

Si vous codez pour OS X 10.6 / iOS 4.0 et supérieur, vous avez également la possibilité d'utiliser des API basées sur des blocs pour énumérer des tableaux et d'autres collections:

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

Vous pouvez aussi utiliser -enumerateObjectsWithOptions:usingBlock: et passer NSEnumerationConcurrent et/ou NSEnumerationReverse, comme l' argument options.


10.4 ou plus tôt

La norme idiome pour pré-10.5 est d'utiliser un NSEnumerator et une boucle while, comme ceci:

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

Je recommande de garder les choses simples. Vous attacher à un type de tableau est inflexible, et l'augmentation de vitesse supposée de l'utilisation de -objectAtIndex: est insignifiante à l'amélioration avec l'énumération rapide sur 10.5+ de toute façon. (L'énumération rapide utilise en fait l'arithmétique du pointeur sur la structure de données sous-jacente et supprime la plupart des frais généraux d'appel de méthode.) L'optimisation prématurée n'est jamais une bonne idée - il en résulte un code messier pour résoudre un problème qui n'est pas votre goulot d'étranglement de toute façon.

Lors de l'utilisation -objectEnumerator, Vous changez très facilement en une autre collection énumérable (comme un NSSet, des clés dans un NSDictionary, etc.), ou même passer à -reverseObjectEnumerator pour énumérer un tableau en arrière, le tout sans autres changements de code. Si le code d'itération est dans une méthode, vous pouvez même passer n'importe quel NSEnumerator et le code n'a même pas à s'en soucier quoi c'est itérant. De plus, un NSEnumerator (au moins ceux fournis par le code Apple) conserve la collection qu'il énumère tant qu'il y a plus d'objets, vous n'avez donc pas à vous soucier de la durée d'existence d'un objet autoreleased.

Peut-être que la plus grande chose qu'un NSEnumerator (ou une énumération rapide) vous protège est d'avoir une collection mutable (tableau ou autre) sous vous à votre insu pendant que vous l'énumérez. Si vous accédez aux objets par index, vous pouvez rencontrer des exceptions étranges ou des erreurs off-by-one (souvent longtemps après que le problème s'est produit) qui peuvent être horribles à déboguer. L'énumération utilisant l'un des idiomes standard a un comportement "fail-fast", donc le problème (causé par un code incorrect) se manifestera immédiatement lorsque vous essayez d'accéder à l'objet suivant après la mutation. Comme les programmes deviennent plus complexes et multithread, ou même dépendent de quelque chose que le code tiers peut modifier, le code d'énumération fragile devient de plus en plus problématique. Encapsulation et abstraction FTW! :-)


632
répondu Quinn Taylor 2018-09-20 21:48:04

Pour OS X 10.4.x et Précédent:

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

Pour OS X 10.5.x (ou iPhone) et au-delà:

for (id myArrayElement in myArray) {
   ...do something useful with myArrayElement
}
121
répondu diederikh 2010-03-02 15:17:46

Les résultats du test et le code source sont ci-dessous (vous pouvez définir le nombre d'itérations de l'application). Le temps est en millisecondes, et chaque entrée est un résultat moyen de l'exécution du test 5-10 fois. J'ai trouvé que Généralement il est précis à 2-3 chiffres significatifs et après cela, il varierait à chaque exécution. Qui donne une marge d'erreur de moins de 1%. Le test était en cours d'exécution sur un iPhone 3G Car c'est la plate-forme cible qui m'intéressait.

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

Les catégories prévues par le cacao pour gestion des ensembles de données (NSDictionary, NSArray, NSSet, etc.) fournir une interface très agréable pour la gestion de l'information, sans avoir à se soucier de la bureaucratie de la gestion de la mémoire, la réaffectation, etc. Bien sûr, cela a un coût bien. Je pense qu'il est assez évident que dire en utilisant un NSArray de NSNumbers va être plus lent qu'un tableau C de flotteurs pour des itérations simples, alors j'ai décidé de faire quelques tests, et les résultats étaient assez choquants! Je ne l'attendais pas à ce mal. Remarque: ces les tests sont effectués sur un iPhone 3G Car c'est la plate-forme cible qui m'intéressait.

Dans ce test, je fais une comparaison des performances d'accès aléatoire très simple entre un c float* et NSArray de NSNumbers

Je crée une boucle simple pour résumer le contenu de chaque tableau et les chronométrer en utilisant mach_absolute_time (). Le NSMutableArray prend en moyenne 400 fois plus de temps!! (pas 400 pour cent, juste 400 fois plus longtemps! c'est 40 000% plus long!).

En-tête:

// Array_Speed_TestViewController.h

/ / Test De Vitesse Du Tableau

/ / Créé par Mehmet Akten le 05/02/2009.

// le droit d'Auteur MSA Visuels Ltd. 2009. Tous droits réservés.

#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

Mise en œuvre:

/ / Array_Speed_TestViewController.m

/ / Test De Vitesse Du Tableau

/ / Créé par Mehmet Akten le 05/02/2009.

// le droit d'Auteur MSA Visuels Ltd. 2009. Tous droits réservés.

    #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

Depuis : memo.tv

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

Disponible depuis l'introduction des blocs, cela permet d'itérer un tableau avec des blocs. Sa syntaxe n'est pas aussi agréable que l'énumération rapide, mais il y a une caractéristique très intéressante: l'énumération simultanée. Si l'ordre d'énumération n'est pas important et que les travaux peuvent être effectués en parallèle sans verrouillage, cela peut fournir une accélération considérable sur un système multicœur. Plus à ce sujet dans l'énumération simultanée section.

[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

L'idée derrière l'énumération rapide est d'utiliser l'accès rapide au tableau C pour optimiser l'itération. Non seulement il est censé être plus rapide que nsenumerator traditionnel, mais Objective-C 2.0 fournit également une syntaxe très concise.

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

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

NSEnumerator

C'est une forme d'itération externe: [myArray objectEnumerator] retourne un objet. Cet objet possède une méthode nextObject que nous pouvons appeler dans une boucle jusqu'à ce qu'il retourne nil

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

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

ObjectAtIndex: énumération

L'utilisation d'une boucle for qui augmente un entier et l'interrogation de l'objet à l'aide de [myArray objectAtIndex:index] est la forme d'énumération la plus basique.

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

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

14
répondu Hitendra Hckr 2014-05-11 15:30:38

Les trois façons sont:

        //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. est le moyen le plus rapide d'exécution, et 3. avec la complétion automatique, oubliez l'écriture de l'enveloppe d'itération.
8
répondu Javier Calatrava Llavería 2015-08-31 07:11:20

Ajouter each méthode dans votre NSArray category, vous allez besoin d'un lot

Code tiré de ObjectiveSugar

- (void)each:(void (^)(id object))block {
    [self enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
        block(obj);
    }];
}
6
répondu onmyway133 2015-01-01 17:14:21

Voici comment vous déclarez un tableau de chaînes et itérez-les:

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

for (int i = 0; i < [langs count]; i++) {
  NSString *lang = (NSString*) [langs objectAtIndex:i];
  NSLog(@"%@, ",lang);
}
1
répondu user2070775 2016-07-20 16:54:58

Faites ceci : -

for (id object in array) 
{
        // statement
}
0
répondu 2017-06-01 07:17:59