Performances de NSEnumerator par rapport à la boucle for dans Cocoa
-
09-06-2019 - |
Question
Je sais que si vous avez une boucle qui modifie le nombre d'éléments dans la boucle, utiliser NSEnumerator sur un ensemble est le meilleur moyen de vous assurer que votre code explose. Cependant, j'aimerais comprendre les compromis de performances entre la classe NSEnumerator. et juste une vieille école de boucle
La solution
Utiliser le nouveau for (... in ...)
La syntaxe en Objective-C 2.0 est généralement le moyen le plus rapide de parcourir une collection car elle peut maintenir un tampon sur la pile et y insérer des lots d'éléments.
En utilisant NSEnumerator
est généralement le moyen le plus lent car il copie souvent la collection en cours d'itération ;pour les collections immuables, cela peut être bon marché (équivalent à -retain
), mais pour les collections mutables, cela peut entraîner la création d'une copie immuable.
Faire votre propre itération - par exemple, en utilisant -[NSArray objectAtIndex:]
- se situera généralement quelque part entre les deux, car même si vous n'aurez pas de surcharge potentielle de copie, vous n'obtiendrez pas non plus de lots d'objets de la collection sous-jacente.
(PS - Cette question devrait être étiquetée comme Objective-C, pas C, puisque NSEnumerator
est une classe Cocoa et le nouveau for (... in ...)
la syntaxe est spécifique à Objective-C.)
Autres conseils
Après avoir effectué le test plusieurs fois, le résultat est presque le même.Chaque bloc de mesure s'exécute 10 fois consécutivement.
Le résultat dans mon cas du plus rapide au plus lent :
- Pour..dans (testPerformanceExample3) (0,006 s)
- Alors que (testPerformanceExample4) (0,026 s)
- Pour(;;) (testPerformanceExample1) (0,027 s)
- Bloc d'énumération (testPerformanceExample2) (0,067 s)
Les boucles for et while sont presque les mêmes.
Le tmp
est un NSArray
qui contient 1 million d'objets de 0 à 999999.
- (NSArray *)createArray
{
self.tmpArray = [NSMutableArray array];
for (int i = 0; i < 1000000; i++)
{
[self.tmpArray addObject:@(i)];
}
return self.tmpArray;
}
Le code complet :
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
MonFichierTest.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
Pour plus d'informations, visitez: Apples "À propos des tests avec Xcode"
Ils sont très similaires.Avec Objective-C 2.0, la plupart des énumérations sont désormais par défaut NSFastEnumeration
qui crée un tampon des adresses de chaque objet de la collection qu'il peut ensuite livrer.La seule étape que vous enregistrez sur la boucle for classique est de ne pas avoir à appeler objectAtIndex:i
à chaque fois à l'intérieur de la boucle.Les éléments internes de la collection que vous énumérez implémentent une énumération rapide sans appel objectAtIndex:i method
.
Le tampon fait partie de la raison pour laquelle vous ne pouvez pas muter une collection pendant que vous énumérez, l'adresse des objets changera et le tampon qui a été construit ne correspondra plus.
En bonus le format en 2.0 est aussi joli que le classique for loop :
for ( Type newVariable in expression ) {
stmts
}
Lisez la documentation suivante pour approfondir :Référence du protocole NSFastEnumeration