464 votes

Comment puis-je effectuer une itération sur un NSArray?

Je suis à la recherche de la norme de l'idiome d'itérer sur un NSArray. Mon code doit être adapté pour OS X 10.4+.

685voto

Quinn Taylor Points 29688

Généralement préféré de code pour 10.5+/iOS.

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

Cette construction est utilisée pour énumérer les objets dans une collection qui est conforme à l' [NSFastEnumeration protocol](Cacao de Référence). Cette approche a un avantage de vitesse, car il stocke des pointeurs à plusieurs objets (obtenu via un seul appel de méthode) dans un tampon et parcourt eux en avançant dans la mémoire tampon à l'aide de l'arithmétique des pointeurs. C'est beaucoup plus rapide que d'appeler -objectAtIndex: chaque passage dans la boucle.

Il est également intéressant de noter que, bien que vous techniquement pouvez utiliser un pour-en boucle à l'étape par le biais d'un NSEnumerator, j'ai trouvé que cela annule pratiquement la totalité de l'avantage en termes de vitesse de rapide énumération. La raison en est que la valeur par défaut NSEnumerator de la mise en œuvre de l' -countByEnumeratingWithState:objects:count: endroits seulement un objet dans la mémoire tampon sur chaque appel.

J'ai signalé ce en radar://6296108 (Rapide énumération de NSEnumerators est très lent), mais il a été renvoyé De ne Pas Être Fixe. La raison en est que rapide énumération pré-extrait un groupe d'objets, et si vous souhaitez énumérer seulement à un point donné dans l'énumérateur (p. ex. jusqu'à un objet particulier est trouvé, ou si la condition est remplie) et d'utiliser le même agent recenseur après la rupture de la boucle, il sera souvent le cas que plusieurs objets ignorées.

Si vous êtes le codage pour OS X 10.6 / iOS 4.0 et ci-dessus, vous avez également la possibilité d'utiliser la fonction de bloc Api pour énumérer les tableaux et d'autres collections:

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

Vous pouvez également utiliser -enumerateObjectsWithOptions:usingBlock: et passent NSEnumerationConcurrent et/ou NSEnumerationReverse que l'argument options.


10.4 ou version antérieure

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

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 tableau est inflexible, et la prétendue augmentation de la vitesse de l'aide de -objectAtIndex: est négligeable à l'amélioration rapide de l'énumération 10.5+ de toute façon. (Rapide énumération utilise l'arithmétique des pointeurs sur les données sous-jacentes de la structure, et supprime la plupart de l'appel de la méthode de surcharge.) L'optimisation prématurée est jamais une bonne idée, il en résulte messier code pour résoudre un problème qui n'est pas le goulot d'étranglement de toute façon.

Lors de l'utilisation d' -objectEnumerator, vous pouvez facilement passer à un autre énumérable collection (comme un NSSet, clés en NSDictionary, etc.), ou même passer à l' -reverseObjectEnumerator d'énumérer un tableau à l'envers, tous n'ayant pas d'autres changements dans le code. Si l'itération code dans une méthode, vous pouvez même passer dans tous NSEnumerator et le code n'a même pas besoin de s'inquiéter de ce que c'est de l'itération. De plus, une NSEnumerator (au moins ceux fournis par Apple code) conserve la collection, il est l'énumération de tant qu'il y a plus d'objets, de sorte que vous n'avez pas à vous soucier de combien de temps un autoreleased objet existera.

Peut-être la plus grande chose qu'un NSEnumerator (ou rapide énumération) vous protège contre c'est d'avoir une mutable collection (tableau ou autre) changement en dessous de vous , à votre insu, alors que vous êtes de l'énumération. Si vous accédez à des objets par index, vous pouvez exécuter dans d'étranges exceptions ou off-by-one erreurs (souvent longtemps après que le problème a eu lieu) qui peut être effrayant de débogage. Dénombrement à l'aide de l'une des expressions idiomatiques a un "fail-fast" comportement, de sorte que le problème (causée par un mauvais code) va se manifester immédiatement lorsque vous essayez d'accéder à l'objet suivant après la mutation a eu lieu. Comme les programmes deviennent plus complexes et multi-thread, ou même dépend de quelque chose que la troisième partie du code peut modifier, fragile énumération code devient de plus en plus problématique. Encapsulation et d'abstraction FTW! :-)


129voto

diederikh Points 17459

Pour OS X 10.4.x et précédentes:

 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
}

16voto

Hitendra Hckr Points 302

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 exprimé en millisecondes, et chaque entrée est une moyenne des résultats de l'exécution du test de 5 à 10 fois. J'ai trouvé que, généralement, il est précis à 2 ou 3 chiffres significatifs et après qu'il varie avec chaque exécution. Qui donne une marge d'erreur de moins de 1%. Le test a été exécuté sur un iPhone 3G que 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 classes fournies par le Cacao pour la manipulation de jeux de données (NSDictionary, NSArray, NSSet etc.) fournir une très belle interface pour la gestion de l'information, sans avoir à vous soucier de la bureaucratie de la gestion de la mémoire, la réaffectation etc. Bien sûr, cela a un coût. Je pense qu'il est assez évident que dire à l'aide d'un NSArray de NSNumbers va être plus lent qu'un C Tableau de flotteurs simple itérations, donc j'ai décidé de faire quelques tests, et les résultats étaient assez choquant! Je ne l'attendais pas à ce mal. Remarque: ces tests sont effectués sur un iPhone 3G que c'est la plate-forme cible qui m'intéressait.

Dans cet essai, je fais très simple d'accès aléatoire la comparaison des performances entre une C float* et NSArray de NSNumbers

J'ai créer une boucle simple de résumer le contenu de chaque tableau et le temps eux à l'aide de mach_absolute_time(). Le NSMutableArray prend en moyenne 400 fois plus longtemps!! (pas de 400 pour cent, à seulement 400 fois plus longtemps! c'est 40,000% de plus!).

En-tête:

// Array_Speed_TestViewController.h

// Tableau De Test De Vitesse

// Créé par Mehmet Akten sur 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

// Tableau De Test De Vitesse

// Créé par Mehmet Akten sur 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

De : memo.tv

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

Disponible depuis l'introduction de blocs, ce qui permet d'itérer un tableau avec des blocs. Sa syntaxe n'est pas aussi beau que rapide énumération, mais il y a une fonction très intéressante: l'énumération. Si l'énumération de l'ordre n'est pas important et les emplois qui peuvent être effectuées en parallèle sans verrouillage, ce qui peut considérablement plus rapide sur un système multi-core. Plus à ce sujet dans la concurrente de l'énumération de l'article.

[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 rapide énumération est d'utiliser C rapide d'accès au tableau d'optimiser l'itération. Non seulement est-il censé être plus rapide que les traditionnels NSEnumerator, mais Objective-C 2.0 fournit également une très concis de la syntaxe.

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

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

NSEnumerator

C'est une forme d'itération externe: [montableau objectEnumerator] renvoie 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'aide d'une boucle for qui augmente d'un nombre entier et l'interrogation de l'objet à l'aide de [montableau objectAtIndex:index] est la forme la plus élémentaire de l'énumération.

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

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X