2009-02-24 12 views
13

Desidero eseguire la stessa azione su più oggetti memorizzati in un NSSet.Cocoa NSArray/NSSet: -makeObjectsPerformSelector: vs enumerazione rapida

Il mio primo tentativo stava usando un'enumerazione veloce:

for (id item in mySetOfObjects) 
    [item action]; 

che funziona piuttosto bene. Poi ho pensato di:

[mySetOfObjects makeObjectsPerformSelector:@selector(action)]; 

E ora, non so che cosa è la scelta migliore. Per quanto ho capito, le due soluzioni sono equivalenti. Ma ci sono argomenti per preferire una soluzione rispetto all'altra?

risposta

21

Direi di utilizzare makeObjectsPerformSelector, poiché consente all'oggetto NSSet di occuparsi della propria indicizzazione, del ciclo e della distribuzione dei messaggi. Le persone che hanno scritto il codice NSSet hanno maggiori probabilità di conoscere il modo migliore per implementare quel particolare ciclo.

Nella peggiore delle ipotesi, implementano semplicemente lo stesso loop e tutto ciò che si ottiene è un codice leggermente più pulito (non è necessario il ciclo di chiusura). Nella migliore delle ipotesi, hanno apportato alcune ottimizzazioni interne e il codice verrà eseguito più velocemente.

L'argomento viene brevemente menzionato nel documento Apple Code Speed Performance, nella sezione "Cicli di srotolamento".

Se sei preoccupato per le prestazioni, la cosa migliore da fare è impostare un programma rapido che esegua qualche selettore sugli oggetti di un set. Ha eseguito diverse milioni di volte e cronometrare la differenza tra i due diversi casi.

+0

Grazie per il link, non conoscevo questo documento! Come dici tu, la sezione "Srotolare i loop" afferma chiaramente che gli sviluppatori di Cocoa hanno fatto ottimizzazioni interne con -makeObjectsPerformSelector: – mouviciel

+0

My pleasure. Ci sono cose interessanti lì dentro. –

2

makeObjectsPerformSelector: potrebbe essere leggermente più veloce, ma dubito che ci sarà qualche differenza pratica il 99% delle volte. Però è un po 'più conciso e leggibile, lo userei per questo motivo.

4

Non userei makeObjectsPerformSelector per il semplice motivo che è il tipo di chiamata che non si vede spesso. Ecco perché, ad esempio, ho bisogno di aggiungere il codice di debug mentre l'array è enumerato, e davvero non puoi farlo con makeObjectsPerformSelector a meno che non cambi il modo in cui il codice funziona in modalità Release che è un vero no no.

for (id item in mySetOfObjects) 
{ 
    #if MY_DEBUG_BUILD 
    if ([item isAllMessedUp]) 
     NSLog(@"we found that wily bug that has been haunting us"); 
    #endif 

    [item action]; 
} 

--Tom

7

anch'io è stato presentato con questa domanda. Trovo nella documentazione di Apple "Collezioni Programmazione Argomenti" sotto "Sets: Unordered Collections of Objects" il seguente:

Il metodo objectEnumerator NSSet consente si traversa elementi del set un per uno. E themakeObjectsPerformSelector: e makeObjectsPerformSelector: withObject: i metodi prevedono l'invio di messaggi a singoli oggetti nel set. Nella maggior parte dei casi , l'enumerazione veloce deve essere utilizzata perché è più veloce e più flessibile rispetto all'utilizzo di un NSEnumerator o il metodo makeObjectsPerformSelector: . Per ulteriori informazioni sull'enumerazione, vedere "Enumerazione: attraversamento di elementi della raccolta ."

Questo mi porta a credere che Fast Enumeration sia ancora il mezzo più efficace per questa applicazione.

1

Se la pura velocità è l'unico problema (ovvero si sta creando un motore di rendering in cui conta ogni piccolo ciclo CPU), il modo più veloce per scorrere tra gli oggetti NSCollection (a partire da iOS 5.0 ~ 6.0) è vari metodi "enumerateObjectsUsingBlock". Non ho idea del perché sia ​​così, ma l'ho provato e sembra che sia così ...

Ho scritto un piccolo test creando raccolte di centinaia di migliaia di oggetti che hanno ciascuno un metodo che riassume una semplice serie di int . Ognuna di queste raccolte è stata costretta a eseguire i vari tipi di iterazione (per ciclo, enumerazione rapida, makeObjectsPerformSelector e enumerateObjectsUsingBlock) milioni di volte, e in quasi tutti i casi i metodi "enumerateObjectsUsingBlock" hanno vinto facilmente nel corso dei test.

L'unico momento in cui questo non era vero era quando la memoria ha cominciato a riempirsi (quando ho iniziato a correre con milioni di oggetti), dopo di che ha cominciato a perdere a "makeObjectsPerformSelector".

Mi dispiace, non ho scattato un'istantanea del codice, ma è un test molto semplice da eseguire, consiglio vivamente di provarlo e vedere di persona. :)

Problemi correlati