Obtendo um erro "Este aplicativo está modificando o mecanismo de execução automática a partir de um encadeamento em segundo plano"?

310

Venho encontrando muito esse erro no meu OS X usando o swift:

"Este aplicativo está modificando o mecanismo de execução automática a partir de um encadeamento em segundo plano, o que pode levar à corrupção do mecanismo e falhas estranhas. Isso causará uma exceção em uma versão futura."

Eu tenho um meu NSWindow e estou trocando visualizações contentViewna janela. Eu recebo o erro quando tento fazer um NSApp.beginSheetna janela ou quando adiciono um subviewna janela. Tentei desativar o material de redimensionamento automático e não tenho nada usando o layout automático. Alguma ideia?

Às vezes está tudo bem e nada acontece, outras vezes quebra totalmente a minha UIe nada carrega

Marca
fonte
2
Por alguma razão, uma excelente resposta abaixo foi excluída: github.com/nrbrook/NBUIKitMainThreadGuard
Fattie
Me salvou duas horas pelo menos. Obrigado @Fattie
oyalhi
right @oyalhi. tenha cuidado ao usá-lo, eu realmente gostei, mas também tive outros problemas - é um campo difícil! espero que ajude!
Fattie
uma pergunta semi-relacionada #
1825

Respostas:

638

Ele precisa ser colocado dentro de um thread diferente que permita que a interface do usuário seja atualizada assim que a execução da função do thread for concluída:

Swift moderno:

DispatchQueue.main.async {
    // Update UI
}

Versões anteriores do Swift, antes do Swift 3.

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

Objetivo-C:

dispatch_async(dispatch_get_main_queue(), ^{
    // code here
});
Marca
fonte
3
Para fazer isso funcionar no Objetivo C, coloque ^ (void) antes de {no bloco de código e um ponto-e-vírgula.
avance

4
Enquanto não dói, ^ (nulo) em vez de apenas ^ não é necessário. A versão Objective-C da resposta está ótima.
Keller

2
Isso funciona bem para mim. O meu problema é: faça uma solicitação de rede e, dentro do bloco de conclusão com sucesso, chamei function para atualizar a interface do usuário. Como o UIKit não é seguro para threads, é necessário despachar de volta para o thread principal para atualizar a interface do usuário.
24416 Rachel Rachel

1
Veja resposta de @Naishta para a solução Swift 3 #
Nathaniel

1
Para Swift 3: DispatchQueue.main.async () { código }, como @Naishta disse
smukamuka
146

Você recebe uma mensagem de erro semelhante ao depurar com instruções de impressão sem usar 'dispatch_async'. Portanto, quando você recebe essa mensagem de erro, é hora de usar

Swift 4

DispatchQueue.main.async { //code }

Swift 3

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

Versões anteriores do Swift

dispatch_async(dispatch_get_main_queue()){ //code }
Naishta
fonte
5
porque a sintaxe está incorreta, deve ser: dispatch_async(dispatch_get_main_queue(), ^{ /* UI related code */ });Editar: atualizei sua resposta, a formatação da sintaxe funciona melhor lá.
Zoltán
1
ou, quando estiver dentro de um fechamento, desça para o mainthread a partir do encadeamento em segundo plano usando: self.performSelectorOnMainThread (Selector ("yourFunction:"), withObject: 'yourArray / yourObject', waitUntilDone: true)
Naishta
1
No seu não, ter um olhar para a sintaxe
Naishta
82

O erro "este aplicativo está modificando o mecanismo de execução automática a partir de um encadeamento em segundo plano" é registrado no console muito tempo após o problema real, portanto, a depuração pode ser difícil sem o uso de um ponto de interrupção.

Usei a resposta de @ markussvensson para detectar meu problema e o encontrei usando este ponto de interrupção simbólico (Depuração> Pontos de interrupção> Criar ponto de interrupção simbólico):

  1. Símbolos: [UIView layoutIfNeeded]ou[UIView updateConstraintsIfNeeded]
  2. Doença: !(BOOL)[NSThread isMainThread]

insira a descrição da imagem aqui

Crie e execute o aplicativo no emulador e replique as etapas que levam à exibição da mensagem de erro (o aplicativo será mais lento que o normal!). O Xcode irá parar o aplicativo e marcar a linha de código (por exemplo, a chamada de uma função) que está acessando a interface do usuário a partir de um encadeamento em segundo plano.

k06a
fonte
3
Hmm. Eu votei positivo, porque parecia fazer sentido. No entanto, a execução não é interrompida e ainda recebo o erro nos meus logs. Existem outras condições que eu poderia usar para definir um ponto de interrupção simbólico?
Sjakelien
No meu caso, ele quebra. No entanto, o rastreamento de pilha não fornece nenhuma dica de qual visão é responsável. E eu não sei como interpretar o código assembler mostradomovq 0x10880ba(%rip), %rsi ; "_wantsReapplicationOfAutoLayoutWithLayoutDirtyOnEntry:"
Reinhard Männer
Não fará com que o aplicativo fique lento a sério ao depurar?
Itachi
@Itachi sim. Não sei como acelerar.
K06a 06/09
2
Começando com o Xcode 9, é um recurso embutido. Apenas certifique-se a opção "Main Thread Checker" é habilitado em "Diagnósticos" guia de configurações do esquema
AndrewPo
24

Ao tentar atualizar um valor do campo de texto ou adicionar uma subvisão dentro de um encadeamento em segundo plano, você pode obter esse problema. Por esse motivo, você deve colocar esse tipo de código no thread principal.

Você precisa agrupar métodos que chamam atualizações da interface do usuário com dispatch_asynch para obter a fila principal. Por exemplo:

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

EDITADO - SWIFT 3:

Agora, podemos fazer isso seguindo o próximo 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
fonte
23

Para mim, essa mensagem de erro teve origem em um banner do Admob SDK.

Consegui rastrear a origem para "WebThread" definindo um ponto de interrupção condicional.

ponto de interrupção condicional para descobrir quem está atualizando a interface do usuário a partir do encadeamento em segundo plano

Consegui me livrar do problema encapsulando a criação do banner com:

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

Não sei por que isso ajudou, pois não consigo ver como esse código foi chamado a partir de um thread não principal.

Espero que possa ajudar alguém.

markussvensson
fonte
1
Eu tive o mesmo problema. Fiquei confuso por que a exceção estava ocorrendo em um WebThread. Fiz a mesma alteração que você e agora funciona. Estou usando uma versão ligeiramente desatualizada do admob sdk. Gostaria de saber se foi corrigido na versão mais recente. Obrigado por isso. Eu não acho que teria encontrado.
Larry
1
A atualização para a versão mais recente da AdMob resolvido este problema para mim
JH95
Eu recebo uma pausa em um ponto de interrupção simbólico como este, mas nenhum código é mostrado. :(
Victor Engel
20

Eu tive esse problema desde a atualização para o iOS 9 SDK quando estava chamando um bloco que fazia atualizações da interface do usuário em um manipulador de conclusão de solicitação assíncrona NSURLConnection. A colocação da chamada de bloco em um dispatch_async usando o dispatch_main_queue resolveu o problema.

Funcionou bem no iOS 8.

spongessuck
fonte
10

Teve o mesmo problema porque eu estava usando performSelectorInBackground.

Bobby
fonte
Não, eu precisava fazer as coisas em segundo plano. Coloquei a chamada NSNotificationCenter dentro do método dispatch_async (dispatch_get_main_queue () e funcionou.
Bobby
Eu estava obtendo dados de um URLSessionDelegate, que depois chamou NSNotification de UIViewController, respondeu à notificação e lá usei DispatchQueue.main.async para mostrar ao usuário se os dados estavam bons ou não. Errado! - A solução é colocar a notificação na fila principal. DispatchQueue.main.async {NotificationCenter.default.post (nome: NSNotification.Name (RawValue: networkNotificationNames.products.rawValue)), objeto: self, userInfo: [networkNotificationNames.products.rawValue: productList])}
iCyberPaul
7

Você não deve alterar a interface do usuário fora do segmento principal! O UIKit não é seguro para threads, de modo que o problema acima e também outros problemas estranhos surgirão se você fizer isso. O aplicativo pode até falhar.

Portanto, para executar as operações do UIKit, você precisa definir o bloco e deixá-lo ser executado na fila principal: como,

NSOperationQueue.mainQueue().addOperationWithBlock {

}
Chowdhury Md Rajib Sarwar
fonte
Isso não está disponível no Xcode 7.2 e iOS 9.2. Alguma outra alternativa?
Jayprakash Dubey
7

Obviamente, você está fazendo alguma atualização da interface do usuário no encadeamento de back ground. Não é possível prever exatamente onde, sem ver seu código.

Estas são algumas situações que podem acontecer: -

você pode estar fazendo algo no thread de segundo plano e não usando. Estando na mesma função, esse código é mais fácil de detectar.

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

chamando um func fazendo chamada de solicitação da web no thread de segundo plano e seu manipulador de conclusão chamando outro func fazendo atualização da interface do usuário. para resolver isso, tente verificar o código em que você atualizou a interface do usuário após a chamada de solicitação da 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()
            }
}
Ashish Pisey
fonte
5

O principal problema com "Este aplicativo está modificando o mecanismo de recuperação automática a partir de um encadeamento em segundo plano" é que ele parece estar registrado muito tempo após a ocorrência do problema real, dificultando a solução de problemas.

Consegui resolver o problema criando três pontos de interrupção simbólicos.

Depuração> Pontos de interrupção> Criar ponto de interrupção simbólico ...

Ponto de interrupção 1:

  • Símbolo: -[UIView setNeedsLayout]

  • Doença: !(BOOL)[NSThread isMainThread]

Ponto de interrupção 2:

  • Símbolo: -[UIView layoutIfNeeded]

  • Doença: !(BOOL)[NSThread isMainThread]

Ponto de interrupção 3:

  • Símbolo: -[UIView updateConstraintsIfNeeded]

  • Doença: !(BOOL)[NSThread isMainThread]

Com esses pontos de interrupção, você pode facilmente obter uma quebra na linha real em que chama incorretamente os métodos de interface do usuário no thread não principal.

www.jensolsson.se
fonte
4

Eu tive esse problema ao recarregar dados no UITableView. O simples envio do recarregamento da seguinte forma corrigiu o problema para mim.

    dispatch_async(dispatch_get_main_queue(), { () -> Void in
        self.tableView.reloadData()
    })
ReshDev
fonte
Foi isso para mim! Eu tinha tudo na fila principal, exceto um reloadData () perdido!
Oprimus
3

Eu tive o mesmo problema. Acontece que eu estava usando o UIAlertsque precisava da fila principal. Mas eles foram preteridos .
Quando mudei UIAlertspara para UIAlertController, não tinha mais o problema e não precisava usar nenhum dispatch_asynccódigo. A lição - preste atenção aos avisos. Eles ajudam mesmo quando você não espera.

epaus
fonte
3

Você já tem a resposta de código correta do @Mark, mas apenas para compartilhar minhas descobertas: O problema é que você está solicitando uma alteração na exibição e assumindo que isso ocorrerá instantaneamente. Na realidade, o carregamento de uma visualização depende dos recursos disponíveis. Se tudo carregar rápido o suficiente e não houver atrasos, você não perceberá nada. Em cenários em que existe algum atraso devido ao processo estar ocupado, etc, o aplicativo se depara com uma situação em que deveria exibir algo, mesmo que ainda não esteja pronto. Portanto, é aconselhável despachar essas solicitações em filas assíncronas para que elas sejam executadas com base na carga.

Mukund Agarwal
fonte
2

Eu tive esse problema quando estava usando o TouchID, se isso ajudar alguém, quebra sua lógica de sucesso, o que provavelmente faz algo com a interface do usuário na fila principal.

Stuart P.
fonte
2

Pode ser algo tão simples quanto definir um valor de campo / rótulo de texto ou adicionar uma subvisão dentro de um encadeamento em segundo plano, o que pode causar alterações no layout de um campo. Certifique-se de que qualquer coisa que você faça com a interface ocorra apenas no thread principal.

Verifique este link: https://forums.developer.apple.com/thread/7399

Kiran P Nair
fonte
2

Eu tive o mesmo problema ao tentar atualizar a mensagem de erro no UILabel no mesmo ViewController (demora um pouco para atualizar os dados ao tentar fazer isso com a codificação normal). Eu usei DispatchQueueno Swift 3 Xcode 8 e funciona.

FN90
fonte
2

Se você deseja capturar esse erro, use a caixa de seleção principal do verificador de encadeamento em pausa. Corrigir é fácil na maioria das vezes, despachando a linha problemática na fila principal.

insira a descrição da imagem aqui

rockdaswift
fonte
1
O que isso faz exatamente? "Main Thread Checker" foi ativado por padrão e eu verifiquei adicionalmente "Thread Sanitizer" e "Pause on issues" para ambos, mas ele ainda gera apenas a mensagem "modificando de um thread em segundo plano" sem adicionar mais informações.
Neph 6/06/19
Pausa a execução do aplicativo no ponto em que a interface do usuário está sendo modificada em um encadeamento em segundo plano.
rockdaswift
Estranho, não fez isso no meu aplicativo (Xcode 10.2.1). Eu tive que adicionar manualmente pontos de interrupção (como descrito aqui ) para fazer uma pausa e me apontar para a linha de código.
Neph 6/06/19
E o que faço para ver esse conjunto de opções?
David Rector
Editar o esquema de seu alvo
rockdaswift
1

Para mim, a questão era a seguinte. Certifique-se de que performSegueWithIdentifier:é realizado no thread principal:

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

Swift 4,

Suponha que, se você estiver chamando algum método usando a fila de operações

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

E suponha que a função searchFavourites seja como,

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

se você chamar todo o código dentro do método "searchFavourites" no thread principal, ele ainda causará um erro se você estiver atualizando alguma interface do usuário.

Este aplicativo está modificando o mecanismo de autolayout a partir de um encadeamento em segundo plano após o acesso ao mecanismo a partir do encadeamento principal.

Então use solução,

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

Para esse tipo de cenário.

Pramod Mais
fonte
1

Aqui, confira esta linha nos logs

$S12AppName18ViewControllerC11Func()ySS_S2StF + 4420

você pode verificar qual a função que está chamando do thread de segundo plano ou para onde você está chamando o método api. Você precisa chamar sua função do thread principal dessa maneira.

DispatchQueue.main.async { func()}

func () é a função que você deseja chamar no resultado do sucesso da chamada da API ou então.

Registra aqui

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
fonte
0

Também encontrei esse problema, vendo uma tonelada dessas mensagens e rastreamentos de pilha sendo impressos na saída, quando redimensionei a janela para um tamanho menor que o valor inicial. Passando muito tempo descobrindo o problema, pensei em compartilhar a solução bastante simples. Uma vez eu tinha ativado Can Draw Concurrentlyum NSTextViewatravés de IB. Isso informa ao AppKit que ele pode chamar o draw(_:)método da visualização de outro thread. Depois de desativá-lo, não recebi mais nenhuma mensagem de erro. Não tive problemas antes de atualizar para o macOS 10.14 Beta, mas, ao mesmo tempo, também comecei a modificar o código para executar o trabalho com a exibição de texto.

Andreas
fonte