2013-01-17 10 views
15

Sono nuovo per GCD e blocchi e mi sto facilitando.GCD - thread principale e di sfondo per l'aggiornamento di UIImageView

Sfondo: Sto lavorando su una routine di caricamento lenta per un UIScrollView utilizzando ALAssetsLibrary. Quando il mio UIScrollView viene caricato, lo popolo con lo aspectRatioThumbnails dei miei ALAssets e quindi mentre l'utente scorre, chiamo la routine di seguito per caricare il fullScreenImage del set ALA attualmente visualizzato. Sembra funzionare.

(se qualcuno ha una procedura di caricamento più pigro per favore pubblica un commento Ho visto tutto quello che ho potuto trovare oltre al video WWDC ma sembra che si occupi di più di piastrellatura o abbia molta più complessità di quella che mi serve)

la mia domanda: io uso un thread in background per gestire il caricamento del fullScreenImage e quando questo è fatto io uso il thread principale per applicarlo al UIImageView. Devo usare il thread principale? Ho visto che tutti gli aggiornamenti UIKit devono essere eseguiti sul thread principale, ma non sono sicuro che ciò si applichi a UIImageView. Stavo pensando che lo fa, dal momento che è un elemento dello schermo, ma poi ho capito che semplicemente non lo sapevo.

- (void)loadFullSizeImageByIndex:(int)index 
{ 
    int arrayIndex = index; 
    int tagNumber = index+1; 
    ALAsset *asset = [self.assetsArray objectAtIndex:arrayIndex]; 

    __weak typeof(self) weakSelf = self; 

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{ 
     UIImage *tmpImage = [[UIImage alloc] initWithCGImage:asset.defaultRepresentation.fullScreenImage]; 

     if ([weakSelf.scrollView viewWithTag:tagNumber] != nil){ 

      dispatch_async(dispatch_get_main_queue(), ^{ 

       if ([weakSelf.scrollView viewWithTag:tagNumber]!= nil){ 
        UIImageView * tmpImageView = (UIImageView*)[weakSelf.scrollView viewWithTag:tagNumber]; 
        tmpImageView.image = tmpImage; 
       } 
      }); 
     } 
    }); 
} 
+0

E 'il rendering dell'immagine quando si mette in thread in background? – iDev

+0

@ACB: appena fatto un test - sì, rende il rendering ma circa 5 volte più lento di chiamarlo dal thread principale (come nel mio codice di esempio). –

+0

Ma come per la documentazione di UIView, dovresti usarlo nella discussione principale. Ho pubblicato quella parte dalla documentazione nella mia risposta. – iDev

risposta

32

Sì, è necessario utilizzare il thread principale ogni volta che stai toccando UIImageView, o qualsiasi altra classe UIKit (se non diversamente specificato, come ad esempio quando si costruisce UIImage s su thread in background).

Un commento sul codice corrente: è necessario assegnare weakSelf a una variabile locale forte prima di utilizzarlo. Altrimenti il ​​tuo condizionale potrebbe passare, ma poi weakSelf potrebbe essere cancellato prima di provare a usarlo. Sarebbe qualcosa di simile

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{ 
    UIImage *tmpImage = [[UIImage alloc] initWithCGImage:asset.defaultRepresentation.fullScreenImage]; 

    __strong __typeof__(weakSelf) strongSelf = weakSelf; 
    if ([strongSelf.scrollView viewWithTag:tagNumber] != nil){ 

     dispatch_async(dispatch_get_main_queue(), ^{ 
      __strong __typeof__(weakSelf) strongSelf = weakSelf; 
      if ([strongSelf.scrollView viewWithTag:tagNumber]!= nil){ 
       UIImageView * tmpImageView = (UIImageView*)[strongSelf.scrollView viewWithTag:tagNumber]; 
       tmpImageView.image = tmpImage; 
      } 
     }); 
    } 
}); 

Tecnicamente non c'è bisogno di fare questo in primo condizionale in coda fondo, perché si sta solo dereferenziazione una volta lì, ma è sempre una buona idea per memorizzare le deboli variabile in una variabile forte prima di toccarla naturalmente.

+0

Debole. Forte. Mi gira la testa! Penso di aver capito quello che stai dicendo. Piccolo followup: nel blocco per la chiamata alla coda principale ho inserito un condizionale aggiuntivo per verificare se esiste una vista con il tag richiesto. Più tardi stavo pensando che è ridondante da quando ho controllato in precedenza. È sbagliato, sì, perché è possibile (se stavo facendo un po 'di shuffling in sottoscheda) che quando la coda principale si attiva è ** più tardi ** e separato dalla coda di background e il primo condizionale. (utente newbie thread qui) –

1

Se è necessario eseguire il rendering dell'immagine in UIImageView, è necessario farlo nel thread principale. Non funzionerà se non lo fai nella coda principale come mostrato nel tuo codice. Lo stesso vale per qualsiasi rendering dell'interfaccia utente.

if ([weakSelf.scrollView viewWithTag:tagNumber]!= nil){ 
    UIImageView * tmpImageView = (UIImageView*)[weakSelf.scrollView viewWithTag:tagNumber]; 
    tmpImageView.image = tmpImage; 
} 

Come da Apple documentation,

Considerazioni Threading: Manipolazioni per l'interfaccia utente dell'applicazione deve avvenire sul thread principale. Pertanto, dovresti sempre chiamare i metodi della classe UIView dal codice in esecuzione nel thread principale della tua applicazione. L'unica volta che questo non può essere strettamente necessario è durante la creazione dell'oggetto stesso, ma tutte le altre manipolazioni devono essere eseguite sul thread principale.

1

Sì, è necessario utilizzare il thread principale, poiché qualsiasi modifica dell'interfaccia utente deve essere eseguita nel thread principale.

Per quanto riguarda l'utilizzo del GCD, viene utilizzato per sfruttare il vantaggio dei multi core sul dispositivo. Per quanto riguarda l'auto forte e debole

forte auto: si potrebbe desiderare un sé forte, perché in te codice

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{ 
(<your class> *) *strongSelf = weakSelf; 
    UIImage *tmpImage = [[UIImage alloc] initWithCGImage:asset.defaultRepresentation.fullScreenImage]; 

    if ([strongSelf.scrollView viewWithTag:tagNumber] != nil){ 

     dispatch_async(dispatch_get_main_queue(), ^{ 

      if ([strongSelf.scrollView viewWithTag:tagNumber]!= nil){ 
       UIImageView * tmpImageView = (UIImageView*)[strongSelf.scrollView viewWithTag:tagNumber]; 
       tmpImageView.image = tmpImage; 
      } 
     }); 
    } 
}); 

dire di avere una visione che effettuare una chiamata API e ci vuole tempo, quindi torni a un'altra vista ma vuoi comunque che l'immagine sia scaricata, quindi usala forte dato che il blocco possiede il sé, quindi l'immagine viene scaricata.

auto debole: se nella situazione descritta sopra non si desidera scaricare l'immagine quando si passa a una vista diversa, utilizzare il sé debole, poiché il blocco non possiede alcun sé.

0

Se non si utilizza strongSelf nel codice suggerito da Kevin Ballard, è possibile che si verifichi un arresto anomalo a causa della debole ricezione di dati.

anche una buona pratica sarebbe quello di controllare anche per i forti essere non nullo al punto di creare

strongSelf = weakSelf 

    if(strongSelf) 
    { 
     // do your stuff here 
    } 
Problemi correlati