14

Sto cercando di sincronizzare il seguente codice nel iOS5:Come dovrei usare GCD dispatch_barrier_async in iOS (sembra per eseguire prima e non dopo altri blocchi)

  1. un oggetto ha un metodo che effettua una richiesta HTTP da cui ottiene alcuni dati, tra cui un URL a un'immagine
  2. una volta arriva l'dati, i dati testuali viene utilizzato per popolare un modello CoreData
  3. , allo stesso tempo, un secondo thread viene inviato asincrona per scaricare l'immagine; questo thread segnala via KVO a un viewController quando l'immagine è già memorizzata nella cache e disponibile nel modello CoreData.
  4. poiché il download dell'immagine richiederà un po 'di tempo, restituiamo immediatamente l'oggetto CoreData che ha tutti gli attributi ma per l'immagine a il chiamante.
  5. Inoltre, al termine del download del secondo thread, è possibile salvare il modello CoreData .

Questo è il codice (semplificato):

- (void)insideSomeMethod 
{ 
    [SomeHTTPRequest withCompletionHandler: 
    ^(id retrievedData) 
    { 
     if(!retrievedData) 
     { 
      handler(nil); 
     } 

     // Populate CoreData model with retrieved Data... 

     dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ 
      NSURL* userImageURL = [NSURL URLWithString:[retrievedData valueForKey:@"imageURL"]]; 
      aCoreDataNSManagedObject.profileImage = [NSData dataWithContentsOfURL:userImageURL]; 
     }); 

     handler(aCoreDataNSManagedObject); 
     [self shouldCommitChangesToModel]; 
    }]; 
} 

- (void)shouldCommitChangesToModel 
{ 
    dispatch_barrier_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ 
     NSError *error = nil; 
     if(![managedObjectContext save:&error]) 
     { 
      // Handle error 
     } 
    }); 
} 

Ma quello che sta succedendo è che la barriera a base di Save-blocco viene sempre eseguito prima del blocco dell'immagine di caricamento. Cioè,

dispatch_barrier_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ 
      NSError *error = nil; 
      if(![managedObjectContext save:&error]) 
      { 
       // Handle error 
      } 
     }); 

eseguito prima:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ 
       NSURL* userImageURL = [NSURL URLWithString:[retrievedData valueForKey:@"imageURL"]]; 
       aCoreDataNSManagedObject.profileImage = [NSData dataWithContentsOfURL:userImageURL]; 
      }); 

Così, ovviamente, non sto davvero dispacciamento del blocco di caricamento delle immagini prima della barriera, o la barriera avrebbe aspettato fino a quando il blocco di caricamento delle immagini è fatto prima dell'esecuzione (quale era la mia intenzione).

Cosa sto sbagliando? come posso essere sicuro che il blocco di caricamento dell'immagine sia accodato prima del blocco barriera?

risposta

27

A prima vista il problema potrebbe essere che si sta distribuendo il blocco barriera su una coda simultanea globale. È possibile utilizzare solo blocchi barriera sulla propria coda concorrente personalizzata. Per i documenti GCD su dispatch_barrier_async, se si invia un blocco a una coda globale, si comporterà come una normale chiamata dispatch_async.

Mike Ash ha un buon post sul blog su blocchi di barriera GCD: http://www.mikeash.com/pyblog/friday-qa-2011-10-14-whats-new-in-gcd.html

Buona fortuna

T

+0

Ci ho pensato più volte ma poi, non i documenti Apple affermano che in iOS non possiamo davvero creare le nostre code? In modo che se io "creo" uno in realtà sto solo ottenendo un alias su uno di quelli globali? – SaldaVonSchwartz

+0

Credo che da iOS5 sia possibile creare le code di invio. – timthetoolman

+0

Sì, l'ho appena provato e ora funziona. Avrei giurato di aver letto che non saremmo riusciti a crearne di propri o che la creazione li ha semplicemente restituito uno di quelli globali, motivo per cui ho scelto uno globale nel mio codice. – SaldaVonSchwartz

3

È necessario creare il proprio coda e non inviare alle code globali come da ADC Documenti

la coda si specifica dovrebbe essere una coda concomitante creati da soli, usando il functio dispatch_queue_create n. Se la coda che si passa a questa funzione è una coda seriale o una delle code simultanee globali , questa funzione si comporta come la funzione dispatch_async .

da https://developer.apple.com/library/ios/documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference.html#//apple_ref/c/func/dispatch_barrier_async.

È possibile creare tonnellate delle proprie code GCD bene. le code gcd sono molto piccole e puoi crearne tantissime senza problemi. Hai solo bisogno di liberarli quando hai finito con loro.

+0

Solo un testa a testa. Se il target di implementazione è iOS6 + e si sta utilizzando ARC, non è necessario rilasciare le code (non è possibile). ARC lo farà per te! –

0

Per quello che sembra cercare di risolvere, dispatch_barrier_async potrebbe non essere la soluzione migliore. Dai un'occhiata alla sezione Migrating Away From Threads della Concurrency Programming Guide. L'utilizzo di dispatch_sync su una propria coda seriale potrebbe risolvere il problema di sincronizzazione. In alternativa, è possibile utilizzare NSOperation e NSOperationQueue. A differenza di GCD, NSOperation ti consente di gestire facilmente le dipendenze (puoi farlo usando GCD, ma può diventare brutto velocemente).

Problemi correlati