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


310

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

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

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

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


2
По какой-то причине отличный ответ ниже был удален: github.com/nrbrook/NBUIKitMainThreadGuard
Толстяк

Спасло меня хотя бы пару часов. Спасибо @Fattie
oyalhi

верно @oyalhi. будьте осторожны с этим, я действительно наслаждался этим, но потом были и другие проблемы - это сложная область! Надеюсь, поможет!
Толстяк

Ответы:


638

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

Современный Свифт:

DispatchQueue.main.async {
    // Update UI
}

Старые версии Swift, предварительно Swift 3.

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

Objective-C:

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

3
Чтобы это работало в Objective C, поставьте ^ (void) перед {в блоке кода и запятой после запятой.
Аванс

4
Хотя это не больно, ^ (void) вместо просто ^ не нужно. Версия ответа Objective-C просто отлично.
Келлер

2
Он отлично работает для меня. Для моей проблемы, сделать сетевой запрос, и внутри блока успешного завершения я вызвал функцию для обновления пользовательского интерфейса. Поскольку UIKit не является потокобезопасным, необходимо отправить обратно в основной поток для обновления пользовательского интерфейса.
Рэйчел

1
Смотрите ответ @Naishta для решения Swift 3
Натаниэль

1
Для Swift 3: DispatchQueue.main.async () { code }, как сказал
@Naishta

146

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

Swift 4

DispatchQueue.main.async { //code }

Свифт 3

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

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

dispatch_async(dispatch_get_main_queue()){ //code }

5
поскольку синтаксис неправильный, должно быть: dispatch_async(dispatch_get_main_queue(), ^{ /* UI related code */ });Редактировать: я обновил его ответ, там лучше работает форматирование синтаксиса.
Золтан

1
или, находясь внутри замыкания, выйти в основную ветку из фонового потока, используя это: self.performSelectorOnMainThread (Selector ("yourFunction:"), withObject: 'yourArray / yourObject', waitUntilDone: true)
Naishta

1
Нет, его нет, взгляните на синтаксис
Найша

82

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

Я использовал ответ @ markussvensson, чтобы обнаружить мою проблему и нашел ее, используя эту символическую точку останова (Debug> Breakpoints> Create Symbolic Breakpoint):

  1. Символы: [UIView layoutIfNeeded]или[UIView updateConstraintsIfNeeded]
  2. Состояние: !(BOOL)[NSThread isMainThread]

введите описание изображения здесь

Создайте и запустите приложение на эмуляторе и скопируйте шаги, которые приводят к выдаче сообщения об ошибке (приложение будет работать медленнее, чем обычно!). Затем Xcode остановит приложение и отметит строку кода (например, вызов функции), которая обращается к пользовательскому интерфейсу из фонового потока.


3
Хм. Я проголосовал за это, потому что это, казалось, имело смысл. Однако выполнение не прерывается, и я все еще получаю ошибку в своих журналах. Существуют ли другие условия, которые я мог бы использовать для установки символической точки останова?
Sjakelien

В моем случае это ломается. Однако трассировка стека не дает мне никаких намеков на то, какое представление отвечает. И я не знаю, как интерпретировать показанный код ассемблераmovq 0x10880ba(%rip), %rsi ; "_wantsReapplicationOfAutoLayoutWithLayoutDirtyOnEntry:"
Рейнхард Меннер

Не приведет ли это к серьезной медленной работе приложения при отладке?
Итачи

@ Итачи, да. Не знаю, как ускорить.
k06a

2
Начиная с Xcode 9 это встроенная функция. Просто убедитесь, что опция «
Проверка основного потока

24

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

Вам нужно обернуть методы, которые вызывают обновления пользовательского интерфейса, в 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"
   }
}

23

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

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

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

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

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

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

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


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

1
Обновление до последней версии AdMob решило эту проблему для меня
JH95

Я получаю перерыв в такой символической точке останова, как этот, но код не отображается. :(
Виктор Энгель

20

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

В iOS 8 все работало нормально.


10

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


Нет, мне нужно было делать вещи в фоновом режиме. Я поместил вызов NSNotificationCenter в метод dispatch_async (dispatch_get_main_queue (), и он сработал.
Бобби,

Я получал данные из URLSessionDelegate, который затем вызывал NSNotification, а UIViewController отвечал на уведомление, и там я использовал DispatchQueue.main.async, чтобы показать пользователю, хороши ли данные или нет. Неправильно! - Решение помещено Уведомление в основную очередь. DispatchQueue.main.async {NotificationCenter.default.post (имя: NSNotification.Name (rawValue: networkNotificationNames.products.rawValue), объект: self, userInfo: [networkNotificationNames.products.rawValue: productList])}
iCyberPaul

7

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

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

NSOperationQueue.mainQueue().addOperationWithBlock {

}

Это не доступно в Xcode 7.2 и iOS 9.2. Любая другая альтернатива?
Джайпракаш Дубей

7

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

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

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

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

5

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

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

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

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

  • Условное обозначение: -[UIView setNeedsLayout]

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

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

  • Условное обозначение: -[UIView layoutIfNeeded]

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

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

  • Условное обозначение: -[UIView updateConstraintsIfNeeded]

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

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


4

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

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

Это было для меня! У меня было все остальное в главной очереди, кроме случайных reloadData ()!
Опримус

3

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


3

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


2

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


2

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

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


2

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


2

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

введите описание изображения здесь


1
Что именно это делает? «Проверка основного потока» была включена по умолчанию, и я дополнительно проверил «Обеззараживатель потока» и «Приостановить выполнение проблем» для обоих, но он по-прежнему выбрасывает только сообщение «изменение из фонового потока», не добавляя в него больше информации.
Неф

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

Странно, это не делало этого в моем приложении (Xcode 10.2.1). Мне пришлось вручную добавить точки останова (как описано здесь ), чтобы заставить его остановиться и указать мне на строку кода.
Неф

И что мне делать, чтобы увидеть этот набор опций?
Дэвид Ректор

Отредактируйте схему вашей цели
rockdaswift

1

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

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

1

Swift 4,

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

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

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

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

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

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

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

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

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


1

Вот проверить эту строку из журналов

$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
)

0

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.