2013-05-24 14 views
7

Mam klasę, która zawiera NSSet. Że obiekt jest nazywany _collectibles oraz w sposobie, robię kopię tego zestawu, aby wykonać pewne przetwarzanie, coś takiego:Bizarre NSSet copying crash

NSSet* collectibleCopy = [_collectibles copy]; 

W praktyce, widzę to regularnie upaść z tej wiadomości:

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

mam rozwiązany przez zmianę powyższy kod do:

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

A teraz już nie mogę odtworzyć takiej katastrofy. Obstawiam, że [copy] jest bardziej efektywny i wolałbym go użyć, ale nie mogę się domyślić, dlaczego jest on kompletnie wygrany!

Aktualizacja: podczas pełnego kontekstu zajęłoby mnóstwo wyjaśnień, klucze do mnie rozwiązania tego było to, że, a, kod został wywołany w ten sposób:

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

[operationQueue addOperation: operation]; 

I że ja, w zasadzie, dokonując kilka rzeczy wolniej, złap aplikację 2 wątkami z 2 wątkami dla kolejki zainicjowanej w ten sposób:

operationQueue.maxConcurrentOperationCount = 1; 

Którego wydało mi się niemożliwe. Wskazówką było to, że drugi wątek był w [NSAutoreleasePool drain], co doprowadziło mnie do przekonania się, że NSOperationQueue może robić autoodpowiedzenie rzeczy kiedykolwiek/jakkolwiek chce.

+0

Nie mogłem odtworzyć Twojego błędu. Czy mógłbyś dodać nieco więcej kodu do kontekstu? – aLevelOfIndirection

+0

to zestaw, który kopiujesz podstawową relację danych? –

+0

Cały kontekst wymagałby ogromnej ilości kodu. :) – GoldenBoy

Odpowiedz

2

Byłoby

NSSet* collectibleCopy = [NSSet setWithSet:_collectibles] 

praca dla Ciebie?

+0

Możliwe, że zgaduję, że powodem, dla którego nie doszło do awarii (patrz moja druga odpowiedź) było coś z semantyki szybkiej iteracji w porównaniu do tego, co NSSet robi dla protokołu NSCopying. – GoldenBoy

2

OK, więc huzzah za to, że to wymyśliłeś.

Podstęp polegał na tym, że ta operacja została wykonana na asynchronicznej NSOperationQueue. TIL, że NSOperationQueues mają AutoreleasePools, ale zostają spuszczone według uznania GCD. W tym przypadku pulę z poprzedniej operacji odpływano jednocześnie w innym wątku, co spowodowało dość nieprzejrzysty równoczesny problem modyfikacji.

Rozwiązanie:

@autoreleasepool wewnątrz bloku, w którym ten kod został wywołany. Powoduje to, że dren zdarza się jako część bloku, a nie asynchronicznie, a mój stan wyścigowy znika.