Появляется сообщение об ошибке "Это приложение изменяет механизм автоматической разметки из фонового потока"?
Я часто сталкивался с этой ошибкой в моей 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 для обнаружения моей проблемы, нашел его с помощью этой символической точки останова:
- Условные обозначения:
[UIView layoutIfNeeded]
или же[UIView updateConstraintsIfNeeded]
- Состояние:
!(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, но в то же время я также начал изменять код для работы с текстовым представлением.