¿Aparece el error "Esta aplicación está modificando el motor de autolayout desde un hilo de fondo"?

310

He estado encontrando mucho este error en mi OS X usando swift:

"Esta aplicación está modificando el motor de reproducción automática desde un subproceso en segundo plano, lo que puede provocar daños en el motor y bloqueos extraños. Esto provocará una excepción en una versión futura".

Tengo mi NSWindow y estoy intercambiando vistas contentViewde la ventana. Recibo el error cuando intento hacer un NSApp.beginSheeten la ventana o cuando agrego un subviewa la ventana. Intenté deshabilitar cosas de tamaño automático, y no tengo nada usando el diseño automático. ¿Alguna idea?

A veces está bien y no pasa nada, otras veces me rompe por completo UIy nada se carga

marca
fuente
2
Por alguna razón, se eliminó una excelente respuesta a continuación: github.com/nrbrook/NBUIKitMainThreadGuard
Fattie
Me ahorró al menos un par de horas. Gracias @Fattie
oyalhi
derecho @oyalhi. tenga cuidado al usarlo, realmente lo disfruté pero también tuve otros problemas: ¡es un campo difícil! ¡Espero eso ayude!
Fattie
una pregunta semi relacionada
Miel

Respuestas:

638

Debe colocarse dentro de un hilo diferente que permita que la IU se actualice tan pronto como se complete la ejecución de la función de hilo:

Swift moderno:

DispatchQueue.main.async {
    // Update UI
}

Versiones anteriores de Swift, pre Swift 3.

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

C objetivo:

dispatch_async(dispatch_get_main_queue(), ^{
    // code here
});
marca
fuente
3
Para hacer que esto funcione en el Objetivo C, coloque ^ (vacío) antes de {en el bloque de código, y un punto y coma después.
avance

44
Si bien no duele, ^ (nulo) en lugar de solo ^ no es necesario. La versión Objective-C de la respuesta está bien.
Keller

2
Funciona bien para mí. Para mi problema es, hacer una solicitud de red, y dentro del bloque de finalización de éxito llamé a la función para actualizar la interfaz de usuario. Dado que UIKit no es seguro para subprocesos, debe enviarlo nuevamente al subproceso principal para actualizar la interfaz de usuario.
Rachel

1
Vea la respuesta de @Naishta para la solución Swift 3
Nathaniel

1
Para Swift 3: DispatchQueue.main.async () { code }, como dijo
@Naishta
146

Obtiene un mensaje de error similar al depurar con sentencias de impresión sin usar 'dispatch_async'. Entonces, cuando recibe ese mensaje de error, es hora de usar

Swift 4

DispatchQueue.main.async { //code }

Swift 3

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

Versiones anteriores de Swift

dispatch_async(dispatch_get_main_queue()){ //code }
Naishta
fuente
55
porque la sintaxis es incorrecta, debería ser: dispatch_async(dispatch_get_main_queue(), ^{ /* UI related code */ });Editar: actualicé su respuesta, el formato de sintaxis funciona mejor allí.
Zoltán
1
o, dentro de un cierre, descienda al hilo principal desde el hilo de fondo usando esto: self.performSelectorOnMainThread (Selector ("yourFunction:"), withObject: 'yourArray / yourObject', waitUntilDone: true)
Naishta
1
No, no es así, eche un vistazo a la sintaxis
Naishta
82

El error "esta aplicación está modificando el motor de reproducción automática desde un subproceso en segundo plano" se registra en la consola mucho después de que ocurriera el problema real, por lo que la depuración puede ser difícil sin usar un punto de interrupción.

Utilicé la respuesta de @ markussvensson para detectar mi problema y lo encontré usando este Punto de interrupción simbólico (Depuración> Puntos de interrupción> Crear punto de interrupción simbólico):

  1. Símbolos: [UIView layoutIfNeeded]o[UIView updateConstraintsIfNeeded]
  2. Condición: !(BOOL)[NSThread isMainThread]

ingrese la descripción de la imagen aquí

Compile y ejecute la aplicación en el emulador y reproduzca los pasos que conducen al mensaje de error (¡la aplicación será más lenta de lo habitual!). Luego, Xcode detendrá la aplicación y marcará la línea de código (por ejemplo, la llamada de una función) que accede a la interfaz de usuario desde un hilo de fondo.

k06a
fuente
3
Hmm Voté esto porque parecía tener sentido. Sin embargo, la ejecución no se interrumpe y sigo recibiendo el error en mis registros. ¿Hay otras condiciones que podría usar para establecer un punto de interrupción simbólico?
Sjakelien
En mi caso, se rompe. Sin embargo, el seguimiento de la pila no me da ninguna pista de qué vista es responsable. Y no sé cómo interpretar el código de ensamblador que se muestramovq 0x10880ba(%rip), %rsi ; "_wantsReapplicationOfAutoLayoutWithLayoutDirtyOnEntry:"
Reinhard Männer
¿No hará que la aplicación se ejecute lentamente en serio al depurar?
Itachi
@Itachi sip. No sé cómo acelerar.
k06a
2
Comenzando con Xcode 9 es una función incorporada. Solo asegúrese de que la opción "Comprobador de subprocesos principales" esté habilitada en la pestaña "Diagnóstico" de la configuración del esquema
AndrewPo
24

Cuando intenta actualizar un valor de campo de texto o agregar una subvista dentro de un hilo de fondo, puede obtener este problema. Por esa razón, debe colocar este tipo de código en el hilo principal.

Debe ajustar los métodos que llaman a las actualizaciones de la interfaz de usuario con dispatch_asynch para obtener la cola principal. Por ejemplo:

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

EDITADO - SWIFT 3:

Ahora, podemos hacerlo siguiendo el siguiente código:

// 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"
   }
}
Jorge Luis Jiménez
fuente
23

Para mí, este mensaje de error se originó en un banner de Admob SDK.

Pude rastrear el origen hasta "WebThread" estableciendo un punto de interrupción condicional.

punto de interrupción condicional para encontrar quién está actualizando la interfaz de usuario desde el hilo de fondo

Luego pude deshacerme del problema encapsulando la creación de Banner con:

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

No sé por qué esto ayudó, ya que no puedo ver cómo se llamó a este código desde un hilo no principal.

Espero que pueda ayudar a cualquiera.

Markussvensson
fuente
1
Yo tuve el mismo problema. Estaba confundido por qué la excepción estaba ocurriendo en un WebThread. Hice el mismo cambio que tú y ahora funciona. Estoy usando una versión un poco desactualizada del admob sdk. Me pregunto si fue corregido en la última versión. Gracias por esto. No creo que lo hubiera encontrado.
Larry
1
La actualización a la última versión de AdMob resolvió este problema para mí
JH95
Tengo un descanso en un punto de interrupción simbólico como este, pero no se muestra el código. :(
Victor Engel
20

Tuve este problema desde que actualicé el SDK de iOS 9 cuando estaba llamando a un bloque que hacía actualizaciones de UI dentro de un controlador de finalización de solicitud asíncrona NSURLConnection. Poner la llamada de bloqueo en un dispatch_async usando dispatch_main_queue resolvió el problema.

Funcionó bien en iOS 8.

esponja
fuente
10

Tuve el mismo problema porque estaba usando performSelectorInBackground.

Poli
fuente
No, necesitaba hacer cosas en el fondo. Puse la llamada NSNotificationCenter dentro del método dispatch_async (dispatch_get_main_queue () y funcionó.
Bobby
Estaba recibiendo datos de un URLSessionDelegate, que luego llamó a NSNotification un UIViewController respondió a la notificación y allí utilicé DispatchQueue.main.async para mostrarle al usuario si los datos eran buenos o no. ¡Incorrecto! - La solución es poner la Notificación en la cola principal. DispatchQueue.main.async {NotificationCenter.default.post (nombre: NSNotification.Name (rawValue: networkNotificationNames.products.rawValue), object: self, userInfo: [networkNotificationNames.products.rawValue: productList])}
iCyberPaul
7

¡No debes cambiar la interfaz de usuario fuera del hilo principal! UIKit no es seguro para subprocesos, por lo que, si lo hace, surgirán problemas anteriores y también otros problemas extraños. La aplicación incluso puede bloquearse.

Entonces, para realizar las operaciones de UIKit, debe definir el bloque y dejar que se ejecute en la cola principal: por ejemplo,

NSOperationQueue.mainQueue().addOperationWithBlock {

}
Chowdhury Md Rajib Sarwar
fuente
Esto no está disponible en Xcode 7.2 y iOS 9.2. ¿Alguna otra alternativa?
Jayprakash Dubey
7

Obviamente está haciendo alguna actualización de la interfaz de usuario en el hilo de fondo. No puedo predecir exactamente dónde, sin ver su código.

Estas son algunas situaciones que pueden ocurrir:

Es posible que esté haciendo algo en el subproceso de fondo y no lo esté utilizando. Al estar en la misma función, este código es más fácil de detectar.

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

llamar a una función que realiza una llamada de solicitud web en el subproceso en segundo plano y su controlador de finalización llamando a otra función que realiza la actualización de la interfaz de usuario Para resolver este problema, intente verificar el código donde ha actualizado la interfaz de usuario después de la llamada webrequest.

// 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()
            }
}
Ashish Pisey
fuente
5

El principal problema con "Esta aplicación está modificando el motor de reproducción automática desde un subproceso en segundo plano" es que parece haberse registrado mucho tiempo después de que se produce el problema real, esto puede dificultar la resolución de problemas.

Logré resolver el problema creando tres puntos de interrupción simbólicos.

Depurar> Puntos de interrupción> Crear punto de interrupción simbólico ...

Punto de interrupción 1:

  • Símbolo: -[UIView setNeedsLayout]

  • Condición: !(BOOL)[NSThread isMainThread]

Punto de interrupción 2:

  • Símbolo: -[UIView layoutIfNeeded]

  • Condición: !(BOOL)[NSThread isMainThread]

Punto de interrupción 3:

  • Símbolo: -[UIView updateConstraintsIfNeeded]

  • Condición: !(BOOL)[NSThread isMainThread]

Con estos puntos de interrupción, puede obtener fácilmente un salto en la línea real donde llama incorrectamente a los métodos de IU en un subproceso no principal.

www.jensolsson.se
fuente
4

Tuve este problema al volver a cargar datos en UITableView. Simplemente el envío de recarga de la siguiente manera solucionó el problema para mí.

    dispatch_async(dispatch_get_main_queue(), { () -> Void in
        self.tableView.reloadData()
    })
ReshDev
fuente
¡Esto fue para mí! ¡Tenía todo lo demás en la cola principal, pero un reloadData () perdido!
Oprimus
3

Yo tuve el mismo problema. Resulta que estaba usando UIAlertsque necesitaba la cola principal. Pero, han sido desaprobados .
Cuando cambié UIAlertsa UIAlertController, ya no tuve el problema y no tuve que usar ningún dispatch_asynccódigo. La lección: preste atención a las advertencias. Ayudan incluso cuando no lo esperas.

épaus
fuente
3

Ya tiene la respuesta de código correcta de @ Mark pero, solo para compartir mis hallazgos: el problema es que está solicitando un cambio en la vista y suponiendo que sucederá al instante. En realidad, la carga de una vista depende de los recursos disponibles. Si todo se carga lo suficientemente rápido y no hay demoras, entonces no nota nada. En escenarios, donde hay algún retraso debido a que el hilo del proceso está ocupado, etc., la aplicación se encuentra con una situación en la que se supone que muestra algo aunque aún no está listo. Por lo tanto, es aconsejable despachar estas solicitudes en colas asíncronas para que se ejecuten en función de la carga.

Mukund Agarwal
fuente
2

Tuve este problema cuando estaba usando TouchID si eso ayuda a alguien más, ajusta su lógica de éxito que probablemente haga algo con la interfaz de usuario en la cola principal.

Stuart P.
fuente
2

Podría ser algo tan simple como configurar un campo de texto / valor de etiqueta o agregar una subvista dentro de un hilo de fondo, lo que puede hacer que cambie el diseño de un campo. Asegúrese de que todo lo que haga con la interfaz solo ocurra en el hilo principal.

Consulte este enlace: https://forums.developer.apple.com/thread/7399

Kiran P Nair
fuente
2

Tuve el mismo problema al intentar actualizar el mensaje de error en UILabel en el mismo ViewController (se tarda un poco en actualizar los datos al intentar hacerlo con la codificación normal). Utilicé DispatchQueueen Swift 3 Xcode 8 y funciona.

FN90
fuente
2

Si desea buscar este error, use la casilla de verificación del subproceso principal en los problemas. Solucionarlo es fácil la mayoría de las veces, enviando la línea problemática en la cola principal.

ingrese la descripción de la imagen aquí

rockdaswift
fuente
1
¿Qué hace esto exactamente? "Comprobador de subprocesos principales" estaba habilitado de forma predeterminada y, además, verifiqué "Desinfectante de subprocesos" y "Pausa en los problemas" para ambos, pero todavía solo arroja el mensaje "Modificar desde un subproceso en segundo plano" sin agregarle más información.
Neph
Pausa la ejecución de la aplicación en el punto donde se modifica la IU en un hilo de fondo.
rockdaswift
Extraño, no hizo eso en mi aplicación (Xcode 10.2.1). Tuve que agregar puntos de interrupción manualmente (como se describe aquí ) para detenerlo y señalarme la línea de código.
Neph
¿Y qué hago para ver este conjunto de opciones?
David Rector
Edite el esquema de su objetivo
rockdaswift
1

Para mí el problema fue el siguiente. Asegúrese de que performSegueWithIdentifier:se realiza en el hilo principal:

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

Swift 4,

Supongamos que si está llamando a algún método usando la cola de operaciones

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

Y supongamos que la función searchFavourites es como,

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

si llama, todo el código dentro del método "searchFavourites" en el hilo principal, todavía dará un error si está actualizando alguna IU en él.

Esta aplicación está modificando el motor de distribución automática desde un subproceso en segundo plano después de acceder al motor desde el subproceso principal.

Entonces usa la solución,

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

Para este tipo de escenario.

Pramod Más
fuente
1

Aquí mira esta línea de los registros

$S12AppName18ViewControllerC11Func()ySS_S2StF + 4420

puede verificar qué función llama desde el subproceso en segundo plano o desde dónde llama al método api, necesita llamar a su función desde el subproceso principal de esta manera.

DispatchQueue.main.async { func()}

func () es esa función a la que desea llamar como resultado del éxito de la llamada api o de lo contrario.

Registra aquí

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
)
Govind Wadhwa
fuente
0

También me encontré con este problema, al ver una tonelada de estos mensajes y rastros de la pila que se imprimen en la salida, cuando cambié el tamaño de la ventana a un tamaño más pequeño que su valor inicial. Al pasar mucho tiempo resolviendo el problema, pensé en compartir la solución bastante simple. Una vez lo había habilitado Can Draw Concurrentlyen un NSTextViewIB. Eso le dice a AppKit que puede llamar al draw(_:)método de la vista desde otro hilo. Después de deshabilitarlo, ya no recibo ningún mensaje de error. No tuve ningún problema antes de actualizar a macOS 10.14 Beta, pero al mismo tiempo, también comencé a modificar el código para realizar el trabajo con la vista de texto.

Andreas
fuente