web-dev-qa-db-fra.com

Vous obtenez une erreur "Cette application modifie le moteur de calcul automatique à partir d'un thread d'arrière-plan"?

Vous avez souvent rencontré cette erreur sous OS X avec Swift: 

"Cette application modifie le moteur d'autolayout à partir d'un thread en arrière-plan, ce qui peut entraîner une corruption du moteur et des blocages bizarres. Cela provoquera une exception dans une version ultérieure."

J'ai un NSWindow et j'échange les vues avec le contentView de la fenêtre. Je reçois le error lorsque j'essaie de faire un NSApp.beginSheet sur la fenêtre ou que j'ajoute un subview à la fenêtre. J'ai essayé de désactiver des trucs autoresize, et je n'ai rien en utilisant la mise en page automatique. Des pensées?

Parfois ça va et rien ne se passe, d'autres fois ça casse totalement mon UI et rien ne se charge

273
Mark

Okay - trouvé la réponse. Il doit être placé dans un autre thread permettant à l'interface utilisateur de se mettre à jour dès que l'exécution de la fonction de thread est terminée:

Swift 3

 DispatchQueue.main.async {
    // Update UI
 }

Swift Version <3

dispatch_async(dispatch_get_main_queue(){
    // code here
})

Version Objective-C

dispatch_async(dispatch_get_main_queue(), ^{
    // code here
});
602
Mark

Vous obtenez un message d'erreur similaire pendant le debugging avec les instructions d'impression sans utiliser 'dispatch_async' Ainsi, lorsque vous obtenez ce message d'erreur, il est temps de l'utiliser 

Swift 4

DispatchQueue.main.async { //code }

Swift 3

DispatchQueue.main.async(){ //code }

Versions antérieures de Swift

dispatch_async(dispatch_get_main_queue()){ //code }
141
Naishta

Utilisé @markussvensson answer pour détecter mon problème, l'a trouvé en utilisant ce point d'arrêt symbolique:

  1. Symboles: [UIView layoutIfNeeded] ou [UIView updateConstraintsIfNeeded]
  2. Condition: !(BOOL)[NSThread isMainThread]

 enter image description here

67
k06a

Lorsque vous essayez de mettre à jour une valeur de champ de texte ou d'ajouter une sous-vue dans un fil d'arrière-plan, vous pouvez obtenir ce problème. Pour cette raison, vous devriez mettre ce genre de code dans le thread principal.

Vous devez envelopper les méthodes qui appellent les mises à jour de l'interface utilisateur avec dispatch_asynch pour obtenir la file d'attente principale. Par exemple:

dispatch_async(dispatch_get_main_queue(), { () -> Void in
   self.friendLabel.text = "You are following \(friendCount) accounts"
})

Édité - Swift 3:

Maintenant, nous pouvons le faire après le code suivant:

// Move to a background thread to do some long running work
DispatchQueue.global(qos: .userInitiated).async {
   // Do long running task here
   // Bounce back to the main thread to update the UI
   DispatchQueue.main.async {
      self.friendLabel.text = "You are following \(friendCount) accounts"
   }
}
24
Jorge Luis Jiménez

Pour moi, ce message d'erreur provenait d'une bannière d'Admob SDK. 

J'ai été en mesure de suivre l'origine sur "WebThread" en définissant un point d'arrêt conditionnel.

 conditional breakpoint to find who is updating ui from background thread

Ensuite, j'ai pu résoudre ce problème en encapsulant la création de la bannière avec:

dispatch_async(dispatch_get_main_queue(), ^{
   _bannerForTableFooter = [[GADBannerView alloc] initWithAdSize:kGADAdSizeSmartBannerPortrait];
   ...
}

Je ne sais pas pourquoi cela a aidé car je ne vois pas comment ce code a été appelé depuis un thread non principal.

J'espère que cela peut aider n'importe qui.

21
markussvensson

Je rencontrais ce problème depuis la mise à jour vers le SDK iOS 9 lorsque j'appelais un bloc qui effectuait des mises à jour de l'interface utilisateur dans un gestionnaire d'achèvement de demande async NSURLConnection. Le fait de placer l'appel de bloc dans un dispatch_async à l'aide de dispatch_main_queue a résolu le problème.

Cela a bien fonctionné sous iOS 8.

19
spongessuck

Avait le même problème parce que j'utilisais performSelectorInBackground.

10
Bobby

Vous ne devez pas changer l'interface utilisateur hors du fil principal! UIKit n'est pas thread-safe, de sorte que le problème ci-dessus ainsi que d'autres problèmes étranges se poseront si vous le faites. L'application peut même se bloquer.

Ainsi, pour effectuer les opérations UIKit, vous devez définir un bloc et le laisser être exécuté sur la file d'attente principale:

NSOperationQueue.mainQueue().addOperationWithBlock {

}

Évidemment, vous faites une mise à jour de l'interface utilisateur sur le fil de fond. Impossible de prédire exactement où, sans voir votre code.

Voici quelques situations qui pourraient arriver: -

vous pourriez faire quelque chose sur le fil de fond et ne pas utiliser. Étant dans la même fonction, ce code est plus facile à repérer.

DispatchQueue.main.async { // do UI update here }

appeler une fonction faisant appel à une requête Web sur le thread d'arrière-plan et son gestionnaire d'achèvement appelant une autre fonction faisant effectuer la mise à jour de l'interface utilisateur .

// Do something on background thread
DispatchQueue.global(qos: .userInitiated).async {
   // update UI on main thread
   DispatchQueue.main.async {
                // Updating whole table view
                self.myTableview.reloadData()
            }
}
6
Ashish Pisey

J'ai eu ce problème lors du rechargement des données dans UITableView. Envoyer simplement reload reload comme suit a résolu le problème pour moi.

    dispatch_async(dispatch_get_main_queue(), { () -> Void in
        self.tableView.reloadData()
    })
4
ReshDev

J'ai eu le même problème. Il s'avère que j'utilisais UIAlerts qui nécessitait la file d'attente principale. Mais ils ont été obsolètes .
Lorsque j'ai remplacé la UIAlerts par la UIAlertController, je n'ai plus eu le problème et je n'ai pas eu à utiliser de code dispatch_async. La leçon - faites attention aux avertissements. Ils aident même quand on ne s'y attend pas.

3
epaus

Vous avez déjà le code de réponse correct de @Mark, mais pour partager mes conclusions: Le problème est que vous demandez un changement de vue et supposez que cela se produira instantanément. En réalité, le chargement d'une vue dépend des ressources disponibles. Si tout se charge assez rapidement et qu'il n'y a pas de retard, vous ne remarquez rien. Dans les scénarios où il y a un délai dû au fait que le thread de processus est occupé, etc., l'application se retrouve dans une situation où elle est supposée afficher quelque chose même si elle n'est pas encore prête. Par conséquent, il est conseillé de répartir ces demandes dans une file d'attente asynchrone afin qu'elles soient exécutées en fonction de la charge. 

3
Mukund Agarwal

J'ai eu le même problème en essayant de mettre à jour le message d'erreur dans UILabel dans le même ViewController (il faut un peu de temps pour mettre à jour les données en essayant de le faire avec un codage normal). J'ai utilisé DispatchQueue dans Swift 3 Xcode 8 et cela fonctionne.

2
FN90

J'avais ce problème lorsque j'utilisais TouchID si cela pouvait aider quelqu'un d'autre, encapsulez votre logique de réussite qui fait probablement quelque chose avec l'interface utilisateur de la file d'attente principale. 

2
Stuart P.

Cela peut être quelque chose d'aussi simple que de définir une valeur de champ de texte/étiquette ou d'ajouter une sous-vue à l'intérieur d'un fil de fond, ce qui peut modifier la disposition d'un champ. Assurez-vous que tout ce que vous faites avec l'interface ne se produit que dans le fil principal.

Vérifiez ce lien: https://forums.developer.Apple.com/thread/7399

2
Kiran P Nair

Le principal problème de "Cette application modifie le moteur de lecture automatique à partir d'un thread d'arrière-plan" est qu'elle semble être enregistrée longtemps après que le problème s'est produit, ce qui peut rendre très difficile le dépannage.

J'ai réussi à résoudre le problème en créant trois points de rupture symboliques.

Débogage> Points d'arrêt> Créer un point d'arrêt symbolique ...

Point d'arrêt 1:

  • Symbole: -[UIView setNeedsLayout]

  • Condition: !(BOOL)[NSThread isMainThread]

Point d'arrêt 2:

  • Symbole: -[UIView layoutIfNeeded]

  • Condition: !(BOOL)[NSThread isMainThread]

Point d'arrêt 3:

  • Symbole: -[UIView updateConstraintsIfNeeded]

  • Condition: !(BOOL)[NSThread isMainThread]

Avec ces points d'arrêt, vous pouvez facilement obtenir une pause sur la ligne réelle où vous appelez de manière incorrecte des méthodes d'interface utilisateur sur un thread non principal.

1
www.jensolsson.se

Swift 4,

Supposons que si vous appelez une méthode en utilisant la file d'attente d'opérations

operationQueue.addOperation({
            self.searchFavourites()
        })

Et supposons que la fonction searchFavourites ressemble à,

func searchFavourites() {
     DispatchQueue.main.async {
                    //Your code
                }
}

si vous appelez, tout le code dans la méthode "searchFavourites" sur le thread principal, donnera toujours une erreur si vous mettez à jour une interface utilisateur. 

Cette application modifie le moteur d'autolayout à partir d'un arrière-plan thread après l'accès au moteur à partir du thread principal.

Alors utilisez la solution,

operationQueue.addOperation({
            DispatchQueue.main.async {
                self.searchFavourites()
            }
        })

Pour ce genre de scénario.

1
Pramod More

Pour moi, le problème était le suivant. Assurez-vous que performSegueWithIdentifier: est exécuté sur le thread principal:

dispatch_async (dispatch_get_main_queue(), ^{
  [self performSegueWithIdentifier:@"ViewController" sender:nil];
});
1
Offek

J'ai également rencontré ce problème en voyant une tonne de ces messages et traces de pile en cours d'impression lorsque j'ai redimensionné la fenêtre à une taille inférieure à sa valeur initiale. Après avoir passé beaucoup de temps à résoudre le problème, je pensais partager la solution plutôt simple. J'avais déjà activé Can Draw Concurrently sur une NSTextView via IB. Cela indique à AppKit qu'il peut appeler la méthode draw(_:) de la vue à partir d'un autre thread. Après l'avoir désactivé, je n'ai plus de message d'erreur. Je n'avais rencontré aucun problème avant la mise à jour vers macOS 10.14 Beta, mais en même temps, j'ai également commencé à modifier le code pour qu'il fonctionne avec la vue texte.

0
Andreas

Si vous souhaitez rechercher cette erreur, utilisez la case à cocher pause du vérificateur de threads principal. La résoudre est facile la plupart du temps, en répartissant la ligne problématique dans la file d'attente principale. 

 enter image description here

0
rockdaswift