Viene visualizzato l'errore "Questa applicazione sta modificando il motore di autolayout da un thread in background"?


310

Ho riscontrato questo errore molto nel mio OS X usando swift:

"Questa applicazione sta modificando il motore di autolayout da un thread in background, che può portare alla corruzione del motore e a strani arresti anomali. Ciò causerà un'eccezione in una versione futura."

Ho una mia NSWindow e sto scambiando in vista con contentViewla finestra. Ottengo l' errore quando provo a fare un NSApp.beginSheetsulla finestra o quando aggiungo un subviewalla finestra. Ho provato a disabilitare le cose di ridimensionamento automatico e non ho nulla usando il layout automatico. qualche idea?

A volte va bene e non succede nulla, altre volte mi spezza totalmente UIe nulla carica


2
Per qualche ragione una risposta eccellente di seguito è stato cancellato: github.com/nrbrook/NBUIKitMainThreadGuard
Fattie

Mi ha salvato almeno un paio d'ore. Grazie @Fattie
oyalhi,

giusto @oyalhi. stai attento a usarlo, mi è davvero piaciuto ma poi ho avuto anche altri problemi - è un campo difficile! spero che sia d'aiuto!
Fattie,

una domanda semi-correlata
Miele

Risposte:


638

Deve essere inserito in un thread diverso che consenta l'aggiornamento dell'interfaccia utente non appena viene completata l'esecuzione della funzione thread:

Modern Swift:

DispatchQueue.main.async {
    // Update UI
}

Versioni precedenti di Swift, pre Swift 3.

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

Objective-C:

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

3
Per farlo funzionare nell'Obiettivo C, metti ^ (void) prima del {nel blocco di codice e delle parole post-virgola.
avance

4
Mentre non fa male, ^ (vuoto) invece di solo ^ non è necessario. La versione Objective-C della risposta va bene.
Keller,

2
Funziona bene per me. Per il mio problema è, fare una richiesta di rete e all'interno del blocco di completamento riuscito ho chiamato la funzione per aggiornare l'interfaccia utente. Poiché UIKit non è thread-safe, è necessario rispedire al thread principale per aggiornare l'interfaccia utente.
Rachel,

1
Vedi la risposta di @Naishta per la soluzione Swift 3
Nathaniel,

1
Per Swift 3: DispatchQueue.main.async () { code }, come diceva
@Naishta

146

Ricevi un messaggio di errore simile durante il debug con istruzioni di stampa senza usare 'dispatch_async' Quindi quando ricevi quel messaggio di errore, è tempo di usare

Swift 4

DispatchQueue.main.async { //code }

Swift 3

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

Versioni precedenti di Swift

dispatch_async(dispatch_get_main_queue()){ //code }

5
perché la sintassi è sbagliata, dovrebbe essere: dispatch_async(dispatch_get_main_queue(), ^{ /* UI related code */ });Modifica: ho aggiornato la sua risposta, la formattazione della sintassi funziona meglio lì.
Zoltán,

1
oppure, all'interno di una chiusura, scendere al thread principale dal thread in background usando questo: self.performSelectorOnMainThread (Selector ("yourFunction:"), conObject: 'yourArray / yourObject', waitUntilDone: true)
Naishta

1
No, non è così, dai un'occhiata alla sintassi
Naishta

82

L'errore "questa applicazione sta modificando il motore di autolayout da un thread in background" viene registrato nella console molto tempo dopo che si è verificato il problema reale, quindi il debug può essere difficile senza utilizzare un breakpoint.

Ho usato la risposta di @ markussvensson per rilevare il mio problema e l'ho trovato usando questo Punto di interruzione simbolico (Debug> Punti di interruzione> Crea punto di interruzione simbolico):

  1. Simboli: [UIView layoutIfNeeded]o[UIView updateConstraintsIfNeeded]
  2. Condizione: !(BOOL)[NSThread isMainThread]

inserisci qui la descrizione dell'immagine

Costruisci ed esegui l'app sull'emulatore e replica i passaggi che portano al lancio del messaggio di errore (l'app sarà più lenta del solito!). Xcode interromperà quindi l'app e segnerà la riga di codice (ad esempio la chiamata di una funzione) che accede all'interfaccia utente da un thread in background.


3
Hmm. Ho votato a favore, perché sembrava avere un senso. Tuttavia, l'esecuzione non si interrompe e continuo a ricevere l'errore nei miei registri. Ci sono altre condizioni che potrei usare per impostare un breakpoint simbolico?
Sjakelien,

Nel mio caso, si rompe. Tuttavia la traccia dello stack non mi dà alcun suggerimento di quale vista sia responsabile. E non so come interpretare il codice assemblatore mostratomovq 0x10880ba(%rip), %rsi ; "_wantsReapplicationOfAutoLayoutWithLayoutDirtyOnEntry:"
Reinhard Männer il

L'app non rallenterà seriamente l'app durante il debug?
Itachi,

@Itachi sì. Non so come accelerare.
k06a,

2
A partire da Xcode 9 è una funzionalità integrata. Assicurati solo che l'opzione "Controllo thread principale" sia abilitata nella scheda "Diagnostica" delle impostazioni dello schema
AndrewPo

24

Quando si tenta di aggiornare un valore del campo di testo o l'aggiunta di una sottoview all'interno di un thread in background, è possibile ottenere questo problema. Per questo motivo, dovresti inserire questo tipo di codice nel thread principale.

È necessario disporre i metodi che richiamano gli aggiornamenti dell'interfaccia utente con dispatch_asynch per ottenere la coda principale. Per esempio:

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

EDITED - SWIFT 3:

Ora possiamo farlo seguendo il codice seguente:

// 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"
   }
}

23

Per me, questo messaggio di errore ha avuto origine da un banner di Admob SDK.

Sono stato in grado di tracciare l'origine su "WebThread" impostando un breakpoint condizionale.

breakpoint condizionale per scoprire chi sta aggiornando l'interfaccia utente dal thread in background

Quindi sono stato in grado di eliminare il problema incapsulando la creazione del banner con:

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

Non so perché questo mi sia stato di aiuto in quanto non riesco a vedere come questo codice è stato chiamato da un thread non principale.

Spero che possa aiutare chiunque.


1
Ho avuto lo stesso problema. Ero confuso perché l'eccezione si stava verificando in un WebThread. Ho apportato le stesse modifiche apportate a te e ora funziona. Sto usando una versione leggermente obsoleta di admob sdk. Mi chiedo se sia stato corretto nell'ultima versione. Grazie per questo. Non penso che l'avrei trovato.
Larry,

1
L'aggiornamento all'ultima versione di AdMob ha risolto questo problema per me
JH95,

Ottengo una pausa in un punto di interruzione simbolico come questo, ma non viene mostrato il codice. :(
Victor Engel,

20

Ho avuto questo problema dall'aggiornamento all'SDK di iOS 9 quando stavo chiamando un blocco che eseguiva gli aggiornamenti dell'interfaccia utente all'interno di un gestore di completamento della richiesta asincrona NSURLConnection. Mettere la chiamata di blocco in dispatch_async usando dispatch_main_queue ha risolto il problema.

Ha funzionato bene in iOS 8.


10

Ho avuto lo stesso problema perché stavo usando performSelectorInBackground.


No, dovevo fare le cose in background. Ho inserito la chiamata NSNotificationCenter all'interno del metodo dispatch_async (dispatch_get_main_queue () e ha funzionato.
Bobby,

Stavo ricevendo dati da un URLSessionDelegate, che poi chiamava NSNotification un UIViewController ha risposto alla notifica e lì ho usato DispatchQueue.main.async per mostrare all'utente se i dati erano buoni o no. Sbagliato! - La soluzione inserisce la notifica nella coda principale. DispatchQueue.main.async {NotificationCenter.default.post (nome: NSNotification.Name (rawValue: networkNotificationNames.products.rawValue), oggetto: self, userInfo: [networkNotificationNames.products.rawValue: productList])}
iCyberPaul

7

Non devi cambiare l'interfaccia utente fuori dal thread principale! UIKit non è thread-safe, quindi se si verifica questo problema sorgeranno anche alcuni problemi di cui sopra. L'app può persino arrestarsi in modo anomalo.

Quindi, per eseguire le operazioni di UIKit, è necessario definire il blocco e lasciarlo eseguito sulla coda principale: come,

NSOperationQueue.mainQueue().addOperationWithBlock {

}

Questo non è disponibile in Xcode 7.2 e iOS 9.2. Qualche altra alternativa?
Jayprakash Dubey,

7

Ovviamente stai facendo un aggiornamento dell'interfaccia utente sul thread di back ground. Non riesco a prevedere esattamente dove, senza vedere il tuo codice.

Queste sono alcune situazioni in cui potrebbe accadere: -

potresti fare qualcosa sul thread in background e non utilizzarlo. Essendo nella stessa funzione questo codice è più facile da individuare.

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

chiamando una funzione Web facendo una richiesta di chiamata sul thread in background e il suo gestore di completamento chiamando altra funzione facendo l'aggiornamento ui. per risolvere questo problema, verifica il codice in cui hai aggiornato l'interfaccia utente dopo la chiamata alla richiesta web.

// 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()
            }
}

5

Il problema principale con "Questa applicazione sta modificando il motore di autolayout da un thread in background" è che sembra essere registrato molto tempo dopo che si è verificato il problema reale, questo può rendere molto difficile la risoluzione dei problemi.

Sono riuscito a risolvere il problema creando tre punti di interruzione simbolici.

Debug> Punti di interruzione> Crea punto di interruzione simbolico ...

Breakpoint 1:

  • Simbolo: -[UIView setNeedsLayout]

  • Condizione: !(BOOL)[NSThread isMainThread]

Breakpoint 2:

  • Simbolo: -[UIView layoutIfNeeded]

  • Condizione: !(BOOL)[NSThread isMainThread]

Breakpoint 3:

  • Simbolo: -[UIView updateConstraintsIfNeeded]

  • Condizione: !(BOOL)[NSThread isMainThread]

Con questi punti di interruzione, è possibile ottenere facilmente un'interruzione sulla riga effettiva in cui si chiamano erroneamente i metodi dell'interfaccia utente sul thread non principale.


4

Ho riscontrato questo problema durante il ricaricamento dei dati in UITableView. Il semplice invio della ricarica come segue ha risolto il problema per me.

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

Questo è stato per me! Avevo tutto il resto in coda ma un reloadData () randagio!
Oprimus,

3

Ho avuto lo stesso problema. Risulta che stavo usando UIAlertsquello che aveva bisogno della coda principale. Ma sono stati deprecati .
Quando ho cambiato il UIAlertsin UIAlertController, non ho più avuto il problema e non ho dovuto usare alcun dispatch_asynccodice. La lezione - presta attenzione agli avvertimenti. Aiutano anche quando non te lo aspetti.


3

Hai già la risposta corretta del codice da @Mark ma, solo per condividere i miei risultati: Il problema è che stai richiedendo un cambiamento nella vista e supponendo che accadrà all'istante. In realtà, il caricamento di una vista dipende dalle risorse disponibili. Se tutto si carica abbastanza rapidamente e non ci sono ritardi, non si nota nulla. Negli scenari, in cui si verificano ritardi dovuti al thread del processo occupato ecc., L'applicazione si imbatte in una situazione in cui si suppone che visualizzi qualcosa anche se non è ancora pronto. Pertanto, è consigliabile inviare queste richieste in una coda asincrona, in modo che vengano eseguite in base al carico.


2

Ho avuto questo problema quando stavo usando TouchID se questo aiuta qualcun altro, a capo della tua logica di successo che probabilmente fa qualcosa con l'interfaccia utente nella coda principale.


2

Potrebbe essere qualcosa di semplice come l'impostazione di un campo di testo / il valore di un'etichetta o l'aggiunta di una sottoview all'interno di un thread di sfondo, che potrebbe cambiare il layout di un campo. Assicurati che tutto ciò che fai con l'interfaccia avvenga solo nel thread principale.

Controlla questo link: https://forums.developer.apple.com/thread/7399


2

Ho avuto lo stesso problema quando provavo ad aggiornare il messaggio di errore in UILabel nello stesso ViewController (ci vuole un po 'di tempo per aggiornare i dati quando provo a farlo con la normale codifica). Ho usato DispatchQueuein Swift 3 Xcode 8 e funziona.


2

Se si desidera cercare questo errore, utilizzare la casella di controllo Pausa controllo thread principale su problemi. Risolverlo è facile la maggior parte delle volte, inviando la linea problematica nella coda principale.

inserisci qui la descrizione dell'immagine


1
Cosa fa esattamente questo? "Controllo thread principale" è stato abilitato per impostazione predefinita e ho inoltre controllato "Disinfettante thread" e "Pausa su problemi" per entrambi, ma genera comunque solo il messaggio "modifica da un thread in background" senza aggiungere ulteriori informazioni.
Neph,

Mette in pausa l'esecuzione dell'app nel punto in cui l'interfaccia utente viene modificata in un thread in background.
rockdaswift,

Strano, non è stato così nella mia app (Xcode 10.2.1). Ho dovuto aggiungere manualmente i punti di interruzione (come descritto qui ) per farlo mettere in pausa e indicarmi la riga di codice.
Neph,

E cosa devo fare per vedere questo set di opzioni?
David Rector,

Modifica lo schema del tuo obiettivo
rockdaswift il

1

Per me il problema era il seguente. Assicurarsi che performSegueWithIdentifier:venga eseguito sul thread principale:

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

1

Swift 4,

Supponiamo, se stai chiamando un metodo usando la coda operazioni

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

Supponiamo che la funzione di ricerca Preferiti sia come

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

se chiami, tutto il codice all'interno del metodo "searchFavourites" sul thread principale, verrà comunque visualizzato un errore se stai aggiornando un'interfaccia utente in esso.

Questa applicazione sta modificando il motore di autolayout da un thread in background dopo l'accesso al motore dal thread principale.

Quindi usa la soluzione,

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

Per questo tipo di scenario.


1

Qui controlla questa riga dai registri

$S12AppName18ViewControllerC11Func()ySS_S2StF + 4420

puoi controllare quale funzione chiama dal thread in background o dove stai chiamando il metodo api devi chiamare la tua funzione dal thread principale in questo modo.

DispatchQueue.main.async { func()}

func () è quella funzione che si desidera chiamare in seguito al successo della chiamata API o altro.

Registra qui

This application is modifying the autolayout engine from a background thread after the engine was accessed from the main thread. This can lead to engine corruption and weird crashes.
 Stack:(
    0   Foundation                          0x00000001c570ce50 <redacted> + 96
    1   Foundation                          0x00000001c5501868 <redacted> + 32
    2   Foundation                          0x00000001c5544370 <redacted> + 540
    3   Foundation                          0x00000001c5543840 <redacted> + 396
    4   Foundation                          0x00000001c554358c <redacted> + 272
    5   Foundation                          0x00000001c5542e10 <redacted> + 264
    6   UIKitCore                           0x00000001f20d62e4 <redacted> + 488
    7   UIKitCore                           0x00000001f20d67b0 <redacted> + 36
    8   UIKitCore                           0x00000001f20d6eb0 <redacted> + 84
    9   Foundation                          0x00000001c571d124 <redacted> + 76
    10  Foundation                          0x00000001c54ff30c <redacted> + 108
    11  Foundation                          0x00000001c54fe304 <redacted> + 328
    12  UIKitCore                           0x00000001f151dc0c <redacted> + 156
    13  UIKitCore                           0x00000001f151e0c0 <redacted> + 152
    14  UIKitCore                           0x00000001f1514834 <redacted> + 868
    15  UIKitCore                           0x00000001f1518760 <redacted> + 104
    16  UIKitCore                           0x00000001f1543370 <redacted> + 1772
    17  UIKitCore                           0x00000001f1546598 <redacted> + 120
    18  UIKitCore                           0x00000001f14fc850 <redacted> + 1452
    19  UIKitCore                           0x00000001f168f318 <redacted> + 196
    20  UIKitCore                           0x00000001f168d330 <redacted> + 144
    21  AppName                        0x0000000100b8ed00 $S12AppName18ViewControllerC11Func()ySS_S2StF + 4420
    22  AppName                        0x0000000100b8d9f4 $S12CcfU0_y10Foundation4DataVSg_So13NSURLResponseCSgs5Error_pSgtcfU_ + 2384
    23  App NAme                        0x0000000100a98f3c $S10Foundation4DataVSgSo13NSURLResponseCSgs5Error_pSgIegggg_So6NSDataCSgAGSo7NSErrorCSgIeyByyy_TR + 316
    24  CFNetwork                           0x00000001c513aa00 <redacted> + 32
    25  CFNetwork                           0x00000001c514f1a0 <redacted> + 176
    26  Foundation                          0x00000001c55ed8bc <redacted> + 16
    27  Foundation                          0x00000001c54f5ab8 <redacted> + 72
    28  Foundation                          0x00000001c54f4f8c <redacted> + 740
    29  Foundation                          0x00000001c55ef790 <redacted> + 272
    30  libdispatch.dylib                   0x000000010286f824 _dispatch_call_block_and_release + 24
    31  libdispatch.dylib                   0x0000000102870dc8 _dispatch_client_callout + 16
    32  libdispatch.dylib                   0x00000001028741c4 _dispatch_continuation_pop + 528
    33  libdispatch.dylib                   0x0000000102873604 _dispatch_async_redirect_invoke + 632
    34  libdispatch.dylib                   0x00000001028821dc _dispatch_root_queue_drain + 376
    35  libdispatch.dylib                   0x0000000102882bc8 _dispatch_worker_thread2 + 156
    36  libsystem_pthread.dylib             0x00000001c477917c _pthread_wqthread + 472
    37  libsystem_pthread.dylib             0x00000001c477bcec start_wqthread + 4
)

0

Ho anche riscontrato questo problema, vedendo un sacco di questi messaggi e le tracce dello stack stampate nell'output, quando ho ridimensionato la finestra a una dimensione inferiore rispetto al suo valore iniziale. Trascorrere molto tempo a capire il problema, ho pensato di condividere la soluzione piuttosto semplice. Una volta avevo abilitato Can Draw Concurrentlysu un NSTextViewIB. Ciò indica ad AppKit che può chiamare il draw(_:)metodo della vista da un altro thread. Dopo averlo disabilitato, non ho più ricevuto messaggi di errore. Non ho riscontrato alcun problema prima di eseguire l'aggiornamento a macOS 10.14 Beta, ma allo stesso tempo ho anche iniziato a modificare il codice per eseguire il lavoro con la visualizzazione del testo.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.