2012-02-10 5 views
16

Apple's multithreading docs non elencare NSIndexPath come thread-safe o no! Come classe immutabile, generalmente mi aspetterei che fosse infallibile.NSIndexPath è thread-safe?

Precedentemente, sono sicuro che la documentazione utilizzata per indicare che le istanze NSIndexPath erano condivise e globalmente univoche. Adesso sembra che sia scomparso, portandomi a sospettare che il design sia stato rivisto per iOS5/Mac OS X 10.7.

Sono molti i rapporti sugli arresti anomali dei clienti su Mac OS X 10.6 (Snow Leopard) che sembrano bloccati nel tentativo di accedere a un percorso dell'indice. Quindi mi chiedo: le istanze attuali sono thread safe, ma la logica per estrarle dalla cache condivisa non lo è? Qualcuno ha qualche intuizione?

Ecco un esempio di stack trace BTW:

Dispatch queue: com.apple.root.default-priority 
0 libobjc.A.dylib 0x96513f29 _cache_getImp + 9 
1 libobjc.A.dylib 0x965158f0 class_respondsToSelector + 59 
2 com.apple.CoreFoundation 0x948bcb49 ___forwarding___ + 761 
3 com.apple.CoreFoundation 0x948bc7d2 _CF_forwarding_prep_0 + 50 
4 com.apple.Foundation 0x994b10c5 -[NSIndexPath compare:] + 93 
5 com.apple.Foundation 0x99415686 _NSCompareObject + 76 
6 com.apple.CoreFoundation 0x948af61c __CFSimpleMergeSort + 236 
7 com.apple.CoreFoundation 0x948af576 __CFSimpleMergeSort + 70 
8 com.apple.CoreFoundation 0x948af38c CFSortIndexes + 252 
9 com.apple.CoreFoundation 0x948fe80d CFMergeSortArray + 125 
10 com.apple.Foundation 0x994153d3 _sortedObjectsUsingDescriptors + 639 
11 com.apple.Foundation 0x994150d8 -[NSArray(NSKeyValueSorting) sortedArrayUsingDescriptors:] + 566 

Per me, questo è un esempio NSIndexPath cercando di confrontarsi a un'istanza deallocato.

+1

Cosa si fa con questi percorsi di indice e dove si verifica l'arresto anomalo? Gli errori di multithreading sono misteriosi, un crash con un 'NSIndexPath' non significa necessariamente che il problema sia in' NSIndexPath'. – hamstergene

+0

Eseguo una richiesta di recupero e quindi ordinai i risultati in base al loro metodo '-indexPath'. Internamente, ogni volta che viene chiamato, quel metodo crea un percorso indice che rappresenta la posizione dell'oggetto nell'albero. E 'il mio sospetto che mi vengano consegnati 'NSIndexPath' condivisi che verranno poi rilasciati poco dopo su un'altra discussione. –

+0

dove si trova NSIndexPath? È una proprietà dell'oggetto recuperato? –

risposta

4

Finora la migliore risposta che ho è come sospetto:

Come di OS X 10.7 e iOS 5, NSIndexPath è thread-safe. Prima di questo, le istanze sono thread-safe perché sono immutabili, ma il recupero condiviso delle istanze esistenti non lo è.

Per il mio metodo che restituisce percorsi indice on-demand, ho fatto questo:

- (NSIndexPath *)indexPath; 
{ 
    NSIndexPath *result = … // create the path as appropriate 

    return [[result retain] autorelease]; 
} 

Da attuazione di tale ultima riga di codice, abbiamo avuto altre segnalazioni di crash da percorsi di indice.

I percorsi dell'indice sono creati da -indexPathByAddingIndex: o +indexPathWithIndex:.

I risultati che sto vedendo mi rendono abbastanza certo che (prima di 10.7/iOS5) questi metodi stanno restituendo un'istanza esistente NSIndexPath.Quella istanza non viene comunque mantenuta dal thread corrente, quindi il thread che per primo ha creato l'istanza (main nel nostro caso) sta rilasciando il path (probabilmente tramite il pop-up del pool di autorelease) e lasciando il nostro thread worker con un puntatore pendente, che si blocca quando viene utilizzato, come mostrato nella domanda.

È tutto un po 'terrificante, perché se la mia analisi è corretta, la danza retain/autorelease che ho aggiunto sta semplicemente sostituendo una condizione di gara con un'altra, meno probabile.

Prima di 10.7/iOS5, posso pensare solo a una soluzione reale: limitare tutta la creazione di percorsi dell'indice al thread principale. Questo potrebbe essere piuttosto lento se tale codice viene chiamato molto, quindi potrebbe essere migliorato, a costo della memoria, mantenendo una sorta di cache di istanza da utilizzare per i thread in background. Se la cache conserva un percorso, allora sai che non sarà deallocato dal thread principale.

1

Apple non elenca in modo specifico NSIndexPath come thread safe, ma dice che le classi immutabili sono generalmente sicure e quelle mutabili generalmente non lo sono. Dato che NSIndexPath è immutabile, è sicuro assumere che sia thread-safe.

Ma "thread safe" non significa che non può causare arresti anomali rilasciati da un thread prima di utilizzarlo su un altro però. Thread safe generalmente significa solo che i suoi metodi di mutator contengono lock per prevenire glitch dovuti a due thread che impostano le proprietà simultaneamente (motivo per cui le classi senza metodi mutator sono generalmente thread-safe, anche se getter e istanze condivise possono anche causare problemi).

Sembra che il tuo errore sia più probabile a causa dell'utilizzo di un pool di autorelease o di un altro meccanismo che causa il rilascio dell'oggetto in un momento fuori dal tuo controllo. Probabilmente dovresti assicurarti che tutti gli oggetti ad accesso simultaneo siano conservati in proprietà di classi longeve in modo da poterne controllare la durata.

Creare un oggetto autoreleased e accedervi da un altro thread dopo aver rimosso tutti i riferimenti forti ad esso è un pericoloso gioco di corse che può causare arresti difficili da tracciare indipendentemente dal fatto che l'oggetto in questione sia "thread" sicuro".

+0

Sì, comprendo la varietà di pericoli nel codice multi-thread. Non sto creando questi oggetti su un thread e mi aspetto che restino su un altro thread. Sto creando gli oggetti chiedendo 'NSIndexPath' per loro sul thread di lavoro. La mia comprensione è che su 10.6 e precedenti, le istanze di 'NSIndexPath' sono condivise a livello globale. Sospetto che questa cache globale non sia thread-safe, piuttosto che le singole istanze. –

+1

È ancora possibile leggere i documenti iOS 4.3/OS 10.6 in Organizer in Xcode se si scaricano i set di documenti precedenti e si eseguono ricerche in tutti i set di documenti. Ho trovato la linea a cui ti riferisci: "Gli oggetti NSIndexPath sono univoci e condivisi.Se esiste già un percorso indice contenente l'indice o gli indici specificati, tale oggetto viene restituito invece di una nuova istanza." E sì, sembra che sia stato rimosso negli ultimi documenti, quindi forse hai ragione su NSIndexPath che non è thread-safe sotto 10.6. –

+0

Aha, ho dimenticato che puoi ancora accedere ai vecchi documenti! –