Появляется ошибка «Это приложение модифицирует механизм автоматической разметки из фонового потока»?

262

Очень часто сталкивалась с этой ошибкой в ​​моей ОС X с помощью быстрой:

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

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

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

  • 2
    По какой-то причине отличный ответ ниже был удален: github.com/nrbrook/NBUIKitMainThreadGuard
  • 0
    Спасло меня хотя бы пару часов. Спасибо @ толстяк
Показать ещё 2 комментария
Теги:
macos
autolayout
background-thread

18 ответов

614
Лучший ответ

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

Swift 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
});
  • 0
    Где-то в вашем коде вы позволяете queue = NSOperationQueue (), которая использует фоновый поток, теперь я не вижу ссылки на то, где я делаю это и куда добавляются мои подпредставления
  • 0
    Предполагается, что основная очередь / поток несет полную ответственность за обновление пользовательского интерфейса и быстрое реагирование на инициируемые пользователем события. Использование фонового потока в пользовательском интерфейсе может вызвать некоторые условия гонки, если (всегда работающий) основной поток пытается что-то обновить одновременно. Вот почему вы получили предупреждение.
Показать ещё 9 комментариев
127

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

Swift 4

DispatchQueue.main.async { //code }

Swift 3

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

Предыдущие версии Swift

dispatch_async(dispatch_get_main_queue()){ //code }
  • 5
    поскольку синтаксис неправильный, должно быть: dispatch_async(dispatch_get_main_queue(), ^{ /* UI related code */ }); Редактировать: я обновил его ответ, там лучше работает форматирование синтаксиса.
  • 0
    у меня синтаксис работает в Xcode 7, swift 2.1
Показать ещё 3 комментария
65

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

  • Символы: [UIView layoutIfNeeded] или [UIView updateConstraintsIfNeeded]
  • Условие: !(BOOL)[NSThread isMainThread]

Изображение 1685

  • 1
    Хм. Я проголосовал за это, потому что это, казалось, имело смысл. Однако выполнение не прерывается, и я все еще получаю ошибку в своих журналах. Существуют ли другие условия, которые я мог бы использовать для установки символической точки останова?
  • 0
    В моем случае это ломается. Однако трассировка стека не дает мне никаких намеков на то, какое представление ответственно. И я не знаю, как интерпретировать показанный код ассемблера movq 0x10880ba(%rip), %rsi ; "_wantsReapplicationOfAutoLayoutWithLayoutDirtyOnEntry:"
Показать ещё 5 комментариев
23

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

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

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

EDITED - 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"
   }
}
21

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

Он отлично работал в iOS 8.

20

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

Мне удалось проследить происхождение до "WebThread", установив условную точку останова.

Изображение 1686

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

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

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

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

  • 1
    У меня такая же проблема. Я был смущен, почему исключение происходило в WebThread. Я сделал то же самое изменение, что и вы, и теперь оно работает. Я использую слегка устаревшую версию адмоба SDK. Интересно, было ли это исправлено в последней версии. Спасибо за это. Я не думаю, что нашел бы это.
  • 1
    Обновление до последней версии AdMob решило эту проблему для меня
Показать ещё 1 комментарий
10

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

  • 0
    вы исправили это с помощью executeSelectorOnMainThread ()?
  • 0
    Нет, мне нужно было делать вещи в фоновом режиме. Я поместил вызов NSNotificationCenter в метод dispatch_async (dispatch_get_main_queue (), и он работал.
Показать ещё 1 комментарий
7

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

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

NSOperationQueue.mainQueue().addOperationWithBlock {

}
  • 0
    Это не доступно в Xcode 7.2 и iOS 9.2. Любая другая альтернатива?
  • 0
    Пожалуйста, посмотрите здесь appcoda.com/ios-concurrency
6

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

Вот некоторые ситуации: -

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

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

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

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

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

    dispatch_async(dispatch_get_main_queue(), { () -> Void in
        self.tableView.reloadData()
    })
  • 0
    Это было для меня! У меня было все остальное в главной очереди, кроме случайных reloadData ()!
3

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

3

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

2

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

2

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

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

2

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

1

Swift 4,

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

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

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

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

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

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

Поэтому используйте решение,

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

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

1

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

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

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

Ещё вопросы

Сообщество Overcoder
Наверх
Меню