2012-01-24 1 views
5

Ci sono 2 scelte (forse più). Utilizzando NSSet come esempio:Modo preferito per creare una copia mutabile di un oggetto non modificabile?

NSMutableSet * mutableSet = [ NSMutableSet setWithSet:nonMutableSet ] ; 

o

NSMutableSet * mutableSet = [ [ nonMutableSet mutableCopy ] autorelease ] ; 

C'è qualche differenza tra questi due implementazioni? Uno è "più efficiente"? (Altri esempi sarebbero NSArray/NSMutableArray e NSDictionary/NSMutableDictionary

+0

Buona domanda, e sospetto che ci potrebbe essere un sacco di dibattito su questo. –

+0

Se vuoi fare un favore a tutti, confrontalo e rispondi tu stesso alla domanda :) Sospetto che non ci dovrebbe essere molta differenza, comunque. – fzwo

+0

@fzwo Immagino di poterlo fare .. Sebbene sia in parte una questione di filosofia di programmazione :) – nielsbot

risposta

7

divertimento Benchmark :)

#import <Foundation/Foundation.h> 

int main (int argc, const char * argv[]) 
{ @autoreleasepool { 
    NSMutableSet *masterSet = [NSMutableSet set]; 

    for (NSInteger i = 0; i < 100000; i++) { 
     [masterSet addObject:[NSNumber numberWithInteger:i]]; 
    } 

    clock_t start = clock(); 

    for (NSInteger i = 0; i < 100; i++) { 
     @autoreleasepool { 
      [NSMutableSet setWithSet:masterSet]; 
     } 
    } 

    NSLog(@"a: --- %lu", clock() - start); 

    sleep(1); 

    start = clock(); 

    for (NSInteger i = 0; i < 100; i++) { 
     @autoreleasepool { 
      [[masterSet mutableCopy] autorelease]; 
     } 
    } 

    NSLog(@"b: --- %lu", clock() - start); 

    return 0; 
} } 

Sulla mia macchina (10.7), setWithSet: è ~ 3 volte più lento di -mutableCopy (C'è qualcuno che vuole provare su iOS 5? :))

Ora, la domanda è: perché?

-mutableCopy sta trascorrendo la maggior parte del tempo in CFBasicHashCreateCopy() (vedere CFBasicHash.m). Sembra che copi direttamente i bucket hash, senza rehashing.

Running Time Self Symbol Name 
256.0ms 61.5% 0.0 -[NSObject mutableCopy] 
256.0ms 61.5% 0.0  -[__NSCFSet mutableCopyWithZone:] 
256.0ms 61.5% 0.0  CFSetCreateMutableCopy 
255.0ms 61.2% 156.0  CFBasicHashCreateCopy 
97.0ms 23.3% 44.0   __CFSetStandardRetainValue 

-setWithSet enumera attraverso ogni valore del set, e quindi aggiungendo al nuovo set. Dall'implementazione di CFBasicHashAddValue (sempre in CFBasicHash.m), sembra che stia rimodellando ogni valore nel set.

Running Time Self Symbol Name 
1605.0ms 86.0% 0.0  +[NSSet setWithSet:] 
1605.0ms 86.0% 2.0  -[NSSet initWithSet:copyItems:] 
1232.0ms 66.0% 68.0  -[__NSPlaceholderSet initWithObjects:count:] 
1080.0ms 57.8% 299.0   CFBasicHashAddValue 
324.0ms 17.3% 28.0  -[NSSet getObjects:count:] 
272.0ms 14.5% 75.0   __CFBasicHashFastEnumeration 

Questo rifacimento ha senso a livello CFSet. CFSet prende un CFSetHashCallBack nel parametro callBacks; quindi, due CFSet di CFNumbers potrebbero avere una routine di hashing diversa specificata. L'NSSet di Foundation utilizza CFSet sotto il cofano e ha una funzione CFSetHashCallBack che richiama -[NSObject hash]. (Anche se penso che Apple potrebbe ottimizzare questo caso ed evitare il rehash quando due set hanno lo stesso callback hash).

Si noti che questo benchmark è solo per NSSet (di NSNumbers), altre classi di raccolta possono avere caratteristiche di prestazione diverse.

+0

Credo che le matrici possano avere diverse implementazioni di supporto (in sottoclassi private del cluster di classi NSArray) a seconda di circostanze come il numero di elementi, che ulteriormente complica il benchmarking (ma generalmente migliora le prestazioni). – fzwo

+1

Sì! Inoltre, il numero di oggetti nella collezione può fare la differenza. Come nota, per gli array che usano il benchmark sopra, sembra che -mutableCopy/-autorelease sia leggermente * più lento * Queste cose sono spesso sintonizzate - è una buona idea rifare i benchmark così spesso che fare ipotesi sulle caratteristiche delle prestazioni di Foundation classi :) – iccir

+0

conta come risposta per me! Grazie. – nielsbot