Отримання помилки "Ця програма змінює двигун автовиведення з фонового потоку" помилку?


310

У моїй ОС X багато стикалися з цією помилкою за допомогою swift:

"Цей додаток модифікує двигун авторозмикання з фонового потоку, що може призвести до пошкодження двигуна та дивних збоїв. Це призведе до виключення в майбутньому випуску."

У мене є моє NSWindow, і я поміняю погляди на contentViewвікно. Я отримую помилку, коли намагаюся робити NSApp.beginSheetвікно або коли я додаю subviewвікно у вікно. Спробували відключити автоматичний розмір матеріалів, і я не маю нічого, що використовує автоматичний макет. Будь-які думки?

Іноді це добре, і нічого не відбувається, інший раз це цілком порушує моє UIі нічим не навантажує


2
З якої - то причини відмінний відповідь нижче був видалений: github.com/nrbrook/NBUIKitMainThreadGuard
— Fattie

Врятувало мене принаймні пару годин. Дякуємо @Fattie
— oyalhi

право @oyalhi. будьте обережні, використовуючи це, мені дуже сподобалось, але потім були й інші проблеми - це важке поле! сподіваюся, що це допоможе!
— Fattie

Відповіді:


638

Його потрібно розмістити всередині іншого потоку, що дозволяє користувальницькому інтерфейсу оновлюватись, як тільки виконання функції потоку завершується:

Сучасний Свіфт:

DispatchQueue.main.async {
    // Update UI
}

Старіші версії Swift, попередньо Swift 3.

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

Завдання-C:

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

3
Щоб зробити цю роботу в Objective C, поставте ^ (void) перед кодом {у блоці коду та післясловниками з комою з комою.
— аванс

4
Хоча це не зашкодить, ^ (недійсне) замість просто ^ не потрібно. Версія відповіді Objective-C просто чудова.
— Келлер

2
Це прекрасно працює для мене. Моя проблема полягає в тому, щоб зробити мережевий запит, і всередині блоку завершення успіху я викликав функцію оновлення інтерфейсу користувача. Оскільки UIKit не є безпечним для потоків, вам потрібно відправити назад до основного потоку, щоб оновити інтерфейс користувача.
— Рейчел

1
Дивіться відповідь @Naishta на рішення Swift 3
— Натаніель

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

146

Ви отримуєте подібне повідомлення про помилку під час налагодження з повідомленнями про друк без використання "dispatch_async". Отож, коли ви отримуєте це повідомлення про помилку, його час використовувати

Швидкий 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
Ні його немає, подивіться на синтаксис
— Naishta

82

Помилка "ця програма модифікує двигун автовиведення з фонового потоку", записується в консоль задовго після того, як сталася фактична проблема, тому налагодження цього може бути важким, не використовуючи точку розриву.

Я використовував відповідь @ markussvensson, щоб виявити свою проблему і виявив її за допомогою цієї символічної точки розриву (налагодження> точки відключення > створити символічну точку розриву ):

  1. Символи: [UIView layoutIfNeeded]або[UIView updateConstraintsIfNeeded]
  2. Хвороба: !(BOOL)[NSThread isMainThread]

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

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


3
Хм. Я підтримав це, бо, здавалося, це має сенс. Однак виконання не відбувається, і я все одно отримую помилку в своїх журналах. Чи є інші умови, які я міг би використати для встановлення символічної точки розриву?
— Sjakelien

У моєму випадку вона все-таки ламається. Однак слід стека не дає мені жодної підказки, який погляд є відповідальним. І я не знаю, як інтерпретувати показаний код асемблераmovq 0x10880ba(%rip), %rsi ; "_wantsReapplicationOfAutoLayoutWithLayoutDirtyOnEntry:"
— Reinhard Männer

Чи не стане це причиною того, що додаток серйозно запускається при налагодженні?
— Ітачі

@Itachi так. Не знаю, як пришвидшити.
— k06a

2
Починаючи з Xcode 9, це вбудована функція. Просто переконайтесь, що параметр "Перевірка головної нитки" включений на вкладці "Діагностика" налаштувань схеми
— AndrewPo

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

Для мене це повідомлення про помилку походить від банера від SDK Admob.

Я зміг відстежити походження до "WebThread", встановивши умовну точку розриву.

умовна точка перерви, щоб знайти, хто оновлює інтерфейс користувача з фонової нитки

Тоді я зміг позбутися проблеми, інкапсулювавши створення банера:

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

Я не знаю, чому це допомогло, оскільки я не можу зрозуміти, як цей код викликався з не-головного потоку.

Сподіваюся, це може допомогти будь-кому.


1
У мене була така ж проблема. Мене збентежило, чому виняток трапляється в WebThread. Я вніс ті самі зміни, що і ти, і це працює зараз. Я використовую трохи застарілу версію SDK admob. Цікаво, чи виправлено це в останній версії. Дякую за це Я не думаю, що я його знайшов би.
— Ларрі

1
Оновлення до останньої версії AdMob вирішило цю проблему для мене
— JH95

Я отримую перерву в символічній точці розриву, як це, але не показано код. :(
— Віктор Енгель

20

У мене виникла ця проблема з моменту оновлення до iOS 9 SDK, коли я викликав блок, який робив оновлення інтерфейсу користувача в оброблювачем завершення запиту NSURLConnection async. Встановлення виклику блоку в 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.ames.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()
    })

Це було для мене! У мене все було в основній черзі, але бродяче перезавантаженняData ()!
— Опримус

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

Якщо ви хочете переслідувати цю помилку, використовуйте прапорець головного потоку на паузі щодо питань. Виправити це легко в більшості випадків, відправляючи проблемну лінію в основній черзі.

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


1
Що саме це робить? "Засіб перевірки головної нитки" було включено за замовчуванням, і я додатково перевірив "Засоби дезінфікування" та "Призупинення питань" для обох, але він все ще передає повідомлення "модифікація з фонового потоку", не додаючи до нього більше інформації.
— Неф

Це призупиняє виконання програми в точці, коли інтерфейс модифікується у фоновому потоці.
— rockdaswift

Дивно, це не було в моєму додатку (Xcode 10.2.1). Мені довелося вручну додати точки перерви (як описано тут ), щоб змусити її призупинятись і вказувати на рядок коду.
— Неф

І що мені робити, щоб побачити цей набір варіантів?
— Давид Ректор

Відредагуйте схему своєї мети
— rockdaswift

1

Для мене питання було таке. Переконайтеся, що performSegueWithIdentifier:виконується на головній нитці:

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

1

Швидкий 4,

Припустимо, якщо ви викликаєте якийсь метод за допомогою черги операцій

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

І припустимо, пошук функцій Favourites такий,

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.