2013-05-24 10 views
7

Ho una classe che contiene un NSSet. Questo oggetto è chiamato _collectibles, e in un metodo, io faccio una copia di tale insieme, al fine di fare un po 'di elaborazione, qualcosa di simile:Bizarre NSSet copiatura incidente

NSSet* collectibleCopy = [_collectibles copy]; 

In pratica, vedo questo in crash regolarmente con questo messaggio:

[__NSPlaceholderSet initWithObjects:count:]: attempt to insert nil object from objects 

ho risolto il problema modificando il codice di cui sopra a:

NSMutableSet* collectibleCopy = [[NSMutableSet alloc] initWithCapacity: [_collectibles count]]; 
for (id thing in _collectibles) { 
    [collectibleCopy addObject: thing]; 
} 

E ora non si ripresenta tale incidente. Scommetto che [copy] è più efficiente, e preferirei usarlo, ma non riesco a capire perché è completamente stordito!

Aggiornamento: mentre il contesto completo avrebbe preso un sacco di spiegazione, le chiavi per me risolvere questo fosse che, una, il codice è stato invocato in questo modo:

NSBlockOperation* operation = [NSBlockOperation blockOperationWithBlock: ^{ 
    [thing doStuff]; 
}]; 

[operationQueue addOperation: operation]; 

E che io ero, in fondo facendo una mucchio di cose più lente, prendere l'applicazione con 2 thread in esecuzione 2 thread per una coda inizializzato così:

operationQueue.maxConcurrentOperationCount = 1; 

ho pensato che impossibile. L'indizio era che il secondo thread era in [NSAutoreleasePool drain], che mi ha portato ad apprendere che NSOperationQueue può fare roba autorelease ogni volta che/comunque vuole.

+0

Non sono riuscito a riprodurre il bug. Potresti postare un po 'più di codice per il contesto? – aLevelOfIndirection

+0

è l'insieme su cui si sta copiando una relazione di dati principale? –

+0

L'intero contesto richiede un'enorme quantità di codice. :) – GoldenBoy

risposta

2

Would

NSSet* collectibleCopy = [NSSet setWithSet:_collectibles] 

lavoro per voi?

+0

Potrebbe essere - sto indovinando il motivo per cui non si è bloccato (vedi la mia altra risposta) era qualcosa sulla semantica di iterazione veloce rispetto a qualsiasi NSSet per il protocollo NSCopying. – GoldenBoy

2

OK, quindi huzzah per aver effettivamente capito questo.

Il trucco qui è che questa operazione è stata eseguita su un asincrono NSOperationQueue. TIL che NSOperationQueues ha AutoreleasePools, ma che vengono prosciugati a discrezione di GCD. In questo caso, il pool di un'operazione precedente veniva scaricato su un altro thread contemporaneamente, causando un problema di modifica simultanea piuttosto opaco.

Soluzione:

@autoreleasepool all'interno del blocco su cui ha ottenuto invocato questo codice. Questo fa sì che lo scarico avvenga come parte del blocco, piuttosto che in modo asincrono, e la mia condizione di competizione scompare.