objective-c cocoa nsenumerator

objective c - Rendimiento de NSEnumerator vs para loop en Cocoa



objective-c (3)

Sé que si tienes un bucle que modifica el recuento de los elementos en el bucle, usar el NSEnumerator en un conjunto es la mejor manera de asegurarte de que tu código explote; sin embargo, me gustaría entender las compensaciones de rendimiento entre la clase NSEnumerator y solo una vieja escuela para bucle


Usar la nueva sintaxis de for (... in ...) en Objective-C 2.0 es generalmente la forma más rápida de iterar sobre una colección porque puede mantener un buffer en la pila y obtener lotes de elementos en ella.

El uso de NSEnumerator generalmente es la forma más lenta porque a menudo copia la colección que se itera; para colecciones inmutables esto puede ser barato (equivalente a -retain ) pero para colecciones mutables puede causar la creación de una copia inmutable.

Hacer tu propia iteración, por ejemplo, usar -[NSArray objectAtIndex:] - generalmente estará en algún punto intermedio porque, aunque no tendrás la posible sobrecarga de copiado, tampoco obtendrás lotes de objetos de la colección subyacente.

(PD: esta pregunta debe etiquetarse como Objective-C, no como C, ya que NSEnumerator es una clase Cocoa y la nueva sintaxis for (... in ...) es específica de Objective-C).


Ellos son muy similares. Con Objective-C 2.0, la mayoría de las enumeraciones ahora son predeterminadas a NSFastEnumeration que crea un buffer de las direcciones para cada objeto en la colección que luego puede entregar. El único paso que guarda sobre el ciclo clásico for for es no tener que llamar a objectAtIndex:i cada vez dentro del ciclo. Las objectAtIndex:i method internas de la colección que está enumerando implementan la enumeración rápida sin llamar objectAtIndex:i method .

El búfer es parte de la razón por la que no puede mutar una colección mientras enumera, la dirección de los objetos cambiará y el búfer que se creó ya no coincidirá.

Como extra, el formato en 2.0 se ve tan bien como el clásico para loop:

for ( Type newVariable in expression ) { stmts }

Lea la siguiente documentación para profundizar: NSFastEnumeration Protocol Reference


Después de ejecutar la prueba varias veces, el resultado es casi el mismo. Cada bloque de medida se ejecuta 10 veces consecutivas.

El resultado en mi caso, del más rápido al más lento:

  1. For..in (testPerformanceExample3) (0.006 sec)
  2. While (testPerformanceExample4) (0.026 seg)
  3. Para (;;) (testPerformanceExample1) ( 0.027 seg)
  4. Bloque de enumeración (testPerformanceExample2) (0.067 seg)

El bucle for y while es casi lo mismo.

El tmp es un NSArray que contiene 1 millón 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 el 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

MyTestfile.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 más información, visite: Apples "Acerca de las pruebas con Xcode"