swift - ¿Aparece el error "Esta aplicación está modificando el motor de autolayout desde un hilo de fondo"?
macos background-thread (21)
¡No debe cambiar la interfaz de usuario fuera del hilo principal! UIKit no es seguro para subprocesos, por lo que surgirán problemas anteriores y también otros problemas extraños si lo hace. La aplicación incluso puede bloquearse.
Por lo tanto, para realizar las operaciones de UIKit, debe definir el bloque y dejar que se ejecute en la cola principal: por ejemplo,
NSOperationQueue.mainQueue().addOperationWithBlock {
}
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 al
contentView
de la ventana.
Recibo el
error
cuando intento hacer una
NSApp.beginSheet
en la ventana, o cuando agrego una
subview
a la ventana.
Intenté deshabilitar las cosas de autoresize, y no tengo nada usando el diseño automático.
¿Alguna idea?
A veces está bien y no pasa nada, otras veces rompe totalmente mi
UI
y nada se carga
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.
Regístrese 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 )
Bien, encontré la respuesta. 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 3
DispatchQueue.main.async {
// Update UI
}
Versión Swift <3
dispatch_async(dispatch_get_main_queue(){
// code here
})
Versión Objective-C
dispatch_async(dispatch_get_main_queue(), ^{
// code here
});
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"
}
}
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):
-
Símbolos:
[UIView layoutIfNeeded]
o[UIView updateConstraintsIfNeeded]
-
Condición
!(BOOL)[NSThread isMainThread]
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.
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 una interrupción en la línea real donde llama incorrectamente a los métodos de IU en un subproceso no principal.
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 }
Obviamente estás haciendo alguna actualización de UI 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()
}
}
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];
});
Para mí, este mensaje de error se originó en un banner del Admob SDK.
Pude rastrear el origen hasta "WebThread" estableciendo un punto de interrupción condicional.
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 subproceso no principal.
Espero que pueda ayudar a cualquiera.
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
Swift 4,
Supongamos que 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.
También encontré 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 había habilitado
Can Draw Concurrently
en un
NSTextView
través de IB.
Eso le dice a AppKit que puede llamar al método
draw(_:)
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.
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).
DispatchQueue
en Swift 3 Xcode 8 y funciona.
Tuve el mismo problema porque estaba usando
performSelectorInBackground
.
Tuve este problema al volver a cargar datos en UITableView. Simplemente enviar la recarga de la siguiente manera solucionó el problema para mí.
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.tableView.reloadData()
})
Tuve este problema cuando estaba usando TouchID si eso ayuda a alguien más, ajusta su lógica de éxito que probablemente hace algo con la interfaz de usuario en la cola principal.
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.
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 subproceso 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 recomendable enviar estas solicitudes en colas asíncronas para que se ejecuten en función de la carga.
Yo tuve el mismo problema.
Resulta que estaba usando
UIAlerts
que necesitaba la cola principal.
Pero, han sido
desaprobados
.
Cuando cambié las
UIAlerts
por
UIAlertController
, ya no tuve el problema y no tuve que usar ningún código
dispatch_async
.
La lección: preste atención a las advertencias.
Ayudan incluso cuando no lo esperas.