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


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.