Появляется сообщение об ошибке "Это приложение изменяет механизм автоматической разметки из фонового потока"?

Я часто сталкивался с этой ошибкой в ​​моей OS X, используя swift:

"Это приложение модифицирует механизм автоматической разметки из фонового потока, что может привести к его повреждению и странным сбоям. Это приведет к исключению в будущем выпуске".

У меня есть мое NSWindow, и я поменялся местами contentView окна. Я получаю ошибку, когда пытаюсь сделать NSApp.beginSheet в окне, или когда я добавляю subview к окну. Пробовал отключать авторазмер, а у меня ничего не получается с помощью авторазметки. Какие-нибудь мысли?

Иногда это нормально и ничего не происходит, иногда это полностью ломает мою UI и ничего не грузит

18 ответов

Решение

Ладно - нашел ответ. Он должен быть помещен в другой поток, который позволяет обновлять пользовательский интерфейс после завершения выполнения функции потока:

Свифт 3

 DispatchQueue.main.async {
    // Update UI
 }

Swift Version <3

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

Версия Objective-C

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

Вы получаете похожее сообщение об ошибке при отладке с помощью операторов печати без использования dispatch_async. Поэтому, когда вы получаете это сообщение об ошибке, его время использовать

Swift 4

DispatchQueue.main.async { //code }

Свифт 3

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

Ранние версии Swift

dispatch_async(dispatch_get_main_queue()){ //code }

Использовал ответ @markussvensson для обнаружения моей проблемы, нашел его с помощью этой символической точки останова:

  1. Условные обозначения: [UIView layoutIfNeeded] или же [UIView updateConstraintsIfNeeded]
  2. Состояние: !(BOOL)[NSThread isMainThread]

При попытке обновить значение текстового поля или добавить подпредставление в фоновом потоке, вы можете получить эту проблему. По этой причине вы должны поместить этот вид кода в основной поток.

Чтобы получить основную очередь, вам нужно обернуть методы, которые вызывают обновления пользовательского интерфейса, в dispatch_asynch. Например:

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

РЕДАКТИРОВАНИЕ - SWIFT 3:

Теперь мы можем сделать это, следуя следующему коду:

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

Для меня это сообщение об ошибке возникло из баннера от Admob SDK.

Я смог отследить источник до "WebThread", установив условную точку останова.

условная точка останова, чтобы найти, кто обновляет пользовательский интерфейс из фонового потока

Тогда я смог избавиться от проблемы, заключив в капсулу создание Banner с помощью:

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

Я не знаю, почему это помогло, так как я не могу понять, как этот код вызывался из не основного потока.

Надеюсь, это может кому-нибудь помочь.

У меня была эта проблема с момента обновления до iOS 9 SDK, когда я вызывал блок, который делал обновления пользовательского интерфейса в обработчике завершения асинхронного запроса NSURLConnection. Помещение вызова блока в dispatch_async с использованием dispatch_main_queue решило проблему.

Это работало нормально в iOS 8.

Основная проблема с "Это приложение изменяет механизм автоматического размещения из фонового потока" заключается в том, что кажется, что оно регистрируется в журнале спустя долгое время после возникновения реальной проблемы, что может затруднить устранение неполадок.

Мне удалось решить проблему, создав три символических точки останова.

Отладка> Точки останова> Создать символическую точку останова...

Точка останова 1:

  • Символ: -[UIView setNeedsLayout]

  • Состояние: !(BOOL)[NSThread isMainThread]

Точка останова 2:

  • Символ: -[UIView layoutIfNeeded]

  • Состояние: !(BOOL)[NSThread isMainThread]

Точка останова 3:

  • Символ: -[UIView updateConstraintsIfNeeded]

  • Состояние: !(BOOL)[NSThread isMainThread]

С помощью этих точек останова вы можете легко получить разрыв фактической строки, где вы неправильно вызываете методы пользовательского интерфейса в неосновном потоке.

Была такая же проблема, потому что я использовал performSelectorInBackground,

Очевидно, вы делаете некоторое обновление пользовательского интерфейса в фоновом потоке. Не могу предсказать, где именно, не видя ваш код.

Вот некоторые ситуации, которые могут произойти:

вы можете делать что-то в фоновом потоке и не использовать. Находясь в той же функции, этот код легче обнаружить.

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

Вызов функции, выполняющей вызов веб-запроса в фоновом потоке, и обработчик ее завершения. Вызов другой функции, выполняющей обновление пользовательского интерфейса. Чтобы решить эту проблему, попробуйте проверить код, в котором вы обновили пользовательский интерфейс после вызова веб-запроса.

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

Вы не должны изменять пользовательский интерфейс вне основного потока! UIKit не является потокобезопасным, так что если вы сделаете это, то возникнет проблема, а также некоторые другие странные проблемы. Приложение может даже аварийно завершить работу.

Итак, чтобы выполнить операции UIKit, вам нужно определить блок и позволить ему выполняться в главной очереди: например,

NSOperationQueue.mainQueue().addOperationWithBlock {

}

У меня была эта проблема при перезагрузке данных в UITableView. Простая отправка перезагрузки, как это было решено для меня.

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

У вас уже есть правильный код ответа от @Mark, но просто для того, чтобы поделиться моими выводами: проблема в том, что вы запрашиваете изменение представления и предполагаете, что это произойдет мгновенно. На самом деле загрузка представления зависит от доступных ресурсов. Если все загружается достаточно быстро и задержек нет, вы ничего не замечаете. В сценариях, где есть какая-либо задержка из-за занятости потока процесса и т. Д., Приложение сталкивается с ситуацией, когда оно должно что-то отображать, даже если оно еще не готово. Следовательно, желательно отправлять эти запросы в асинхронных очередях, чтобы они выполнялись в зависимости от нагрузки.

У меня такая же проблема. Оказывается, я использовал UIAlerts для этого нужна была основная очередь. Но они устарели.
Когда я изменил UIAlerts к UIAlertControllerУ меня больше не было проблемы, и мне не пришлось использовать dispatch_async код. Урок - обратите внимание на предупреждения. Они помогают, даже когда вы этого не ожидаете.

У меня возникла та же проблема при попытке обновить сообщение об ошибке в UILabel в том же ViewController (для обновления данных требуется некоторое время при попытке сделать это с помощью обычного кодирования). я использовал DispatchQueue в Swift 3 Xcode 8 и все работает.

Это может быть что-то простое, например, установка значения текстового поля / метки или добавление подпредставления в фоновом потоке, что может привести к изменению макета поля. Убедитесь, что все, что вы делаете с интерфейсом, происходит только в основном потоке.

Проверьте эту ссылку: https://forums.developer.apple.com/thread/7399

Если вы хотите отследить эту ошибку, используйте флажок pause on Issues для проверки основного потока. В большинстве случаев это легко исправить, отправив проблемную строку в основную очередь.

У меня была эта проблема, когда я использовал TouchID, если это помогает кому-то еще, оберните вашу логику успеха, которая, вероятно, что-то делает с пользовательским интерфейсом в основной очереди.

Вот посмотрите эту строчку из логов

$S12AppName18ViewControllerC11Func()ySS_S2StF + 4420

вы можете проверить, какая функция вызывается либо из фонового потока, либо из того места, где вы вызываете метод api, вам нужно вызвать вашу функцию из основного потока следующим образом.

DispatchQueue.main.async { func()}

func() - это функция, которую вы хотите вызвать в результате успешного вызова api или иначе.

Журналы здесь

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
)

Для меня проблема заключалась в следующем. Удостовериться performSegueWithIdentifier: выполняется в основном потоке:

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

Swift 4,

Предположим, если вы вызываете какой-либо метод, используя очередь операций

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

И предположим, что функция searchFavourites похожа,

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

если вы вызовете весь код внутри метода "searchFavourites" в главном потоке, он все равно выдаст ошибку, если вы обновите в нем какой-либо пользовательский интерфейс.

Это приложение модифицирует механизм автоматической разметки из фонового потока после обращения к нему из основного потока.

Так что используйте решение,

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

Для такого сценария.

Я также столкнулся с этой проблемой, увидев тонну этих сообщений и следов стека, напечатанных в выводе, когда я изменил размер окна до меньшего размера, чем его первоначальное значение. Потратив долгое время на выяснение проблемы, я решил поделиться довольно простым решением. Я когда-то включил Can Draw Concurrently на NSTextView через IB. Это говорит AppKit, что он может вызывать представление draw(_:) метод из другого потока. После его отключения я больше не получаю никаких сообщений об ошибках. У меня не возникло никаких проблем до обновления до MacOS 10.14 Beta, но в то же время я также начал изменять код для работы с текстовым представлением.

Другие вопросы по тегам