Je suis nouveau dans GCD, je bloque et je me fraye un chemin.
Arrière-plan: Je travaille sur une routine de chargement paresseux pour UIScrollView à l'aide de ALAssetsLibrary. Lorsque UIScrollView est chargé, je le remplis avec la aspectRatioThumbnails
de mes ALAssets, puis, lorsque l'utilisateur fait défiler l'écran, j'appelle la routine ci-dessous pour charger la fullScreenImage
de l'ALAsset actuellement affiché. Cela semble fonctionner.
(Si quelqu'un a une meilleure routine de chargement paresseuse, postez un commentaire. J'ai tout regardé, plus la vidéo de WWDC, mais ils semblent traiter davantage de mosaïque ou ont beaucoup plus de complexité que nécessaire)
Ma question: / J'utilise un thread d'arrière-plan pour gérer le chargement de la fullScreenImage
et, lorsque cela est fait, j'utilise le thread principal pour l'appliquer à UIImageView. Dois-je utiliser le fil principal? J'ai vu que toutes les mises à jour UIKit devaient se produire sur le fil principal, mais je ne suis pas sûr que cela s'applique à UIImageView. Je pensais que oui, puisqu'il s'agit d'un élément d'écran, mais j'ai alors réalisé que je ne savais tout simplement pas.
- (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;
}
});
}
});
}
Oui, vous devez utiliser le thread principal chaque fois que vous touchez UIImageView
ou toute autre classe UIKit (sauf indication contraire, comme lors de la construction de UIImage
s sur des threads d'arrière-plan).
Un commentaire sur votre code actuel: vous devez assigner weakSelf
à une variable locale forte avant de l’utiliser. Sinon, votre conditionnel pourrait être accepté, mais weakSelf
pourrait être éliminé avant que vous ne l'utilisiez réellement. Ça ressemblerait à quelque chose comme
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;
}
});
}
});
Techniquement, vous n'avez pas besoin de faire cela dans la première condition de la file d'attente en arrière-plan, car vous ne la déréférenciez qu'une fois, mais il est toujours judicieux de stocker votre variable faible dans une variable forte avant de la toucher. cours.
Oui, vous devez utiliser le thread principal, car toute modification de l'interface utilisateur doit être effectuée dans le thread principal.
Quant à l’utilisation du GCD, il permet de tirer avantage des cœurs multiples de l’appareil. En ce qui concerne le moi fort et faible.
strong self : vous voudrez peut-être un moi fort, car en votre code
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;
}
});
}
});
disons que vous avez une vue qui passe un appel d'API et que cela prend du temps, vous revenez donc à une autre vue, mais vous souhaitez tout de même que l'image soit téléchargée, puis utilisez fort, car le bloc possède le moi afin de télécharger l'image.
moi faible : si, dans la situation ci-dessus, vous ne voulez pas que l'image soit téléchargée une fois que vous avez changé d'affichage, utilisez un soi faible, car le bloc ne possède aucun soi.
Si vous devez rendre l'image dans UIImageView
, vous devez le faire dans le fil principal. Cela ne fonctionnera que si vous le faites en file d'attente principale, comme indiqué dans votre code. Même est le cas avec n'importe quel rendu d'interface utilisateur.
if ([weakSelf.scrollView viewWithTag:tagNumber]!= nil){
UIImageView * tmpImageView = (UIImageView*)[weakSelf.scrollView viewWithTag:tagNumber];
tmpImageView.image = tmpImage;
}
Selon Documentation Apple ,
Considérations sur les threads: Les manipulations sur l’interface utilisateur de votre application doivent avoir lieu sur le thread principal. Ainsi, vous devriez toujours appeler Les méthodes de la classe UIView à partir du code exécuté dans le thread principal De votre application. La seule fois où cela peut ne pas être strictement nécessaire Est lors de la création de l'objet de vue lui-même, mais toutes les autres manipulations Doivent avoir lieu sur le thread principal.
Si vous ne voulez pas utiliser strongSelf dans le code suggéré par Kevin Ballard, cela pourrait entraîner un blocage en raison de la faiblesse de la suppression.
Aussi, une bonne pratique serait même de vérifier si le caractère fort est non nul au moment de créer
strongSelf = weakSelf
if(strongSelf)
{
// do your stuff here
}