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
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:
DispatchQueue.main.async {
// Update UI
}
dispatch_async(dispatch_get_main_queue(){
// code here
})
dispatch_async(dispatch_get_main_queue(), ^{
// code here
});
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 }
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"
})
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"
}
}
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.
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.
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.
Avait le même problème parce que j'utilisais performSelectorInBackground
.
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()
}
}
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()
})
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.
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.
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.
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.
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
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.
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.
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];
});
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.