Mendapat kesalahan "Aplikasi ini memodifikasi mesin autolayout dari utas latar"?

310

Telah sering menemui kesalahan ini di OS X saya menggunakan swift:

"Aplikasi ini memodifikasi mesin autolayout dari utas latar belakang, yang dapat menyebabkan kerusakan mesin dan crash aneh. Ini akan menyebabkan pengecualian dalam rilis mendatang."

Saya memiliki NSWindow saya dan saya bertukar pandangan ke contentViewjendela. Saya mendapatkan kesalahan ketika saya mencoba dan melakukan NSApp.beginSheetdi jendela, atau ketika saya menambahkan subviewke jendela. Sudah mencoba menonaktifkan hal-hal autoresize, dan saya tidak punya apa-apa menggunakan tata letak otomatis. Adakah pikiran?

Terkadang baik-baik saja dan tidak ada yang terjadi, di lain waktu itu benar-benar menghancurkan saya UIdan tidak ada beban

Menandai
sumber
2
Untuk beberapa alasan, jawaban yang sangat baik di bawah ini telah dihapus: github.com/nrbrook/NBUIKitMainThreadGuard
Fattie
Setidaknya menyelamatkan saya beberapa jam. Terima kasih @ Fatie
oyalhi
benar @ loyalhi. hati-hati menggunakannya, saya benar-benar menikmatinya tetapi kemudian memiliki masalah lain juga - ini adalah bidang yang sulit! semoga membantu!
Fattie
a terkait setengah pertanyaan
Madu

Jawaban:

638

Itu harus ditempatkan di dalam utas berbeda yang memungkinkan UI untuk memperbarui segera setelah fungsi utas selesai:

Modern Swift:

DispatchQueue.main.async {
    // Update UI
}

Versi Swift yang lebih lama, pra Swift 3.

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

Tujuan-C:

dispatch_async(dispatch_get_main_queue(), ^{
    // code here
});
Menandai
sumber
3
Untuk membuatnya bekerja di Objective C, letakkan ^ (void) sebelum {dalam blok kode, dan kata penutup titik koma.
avance

4
Meskipun tidak sakit, ^ (batal) bukan hanya ^ tidak perlu. Versi Objective-C dari jawabannya baik-baik saja.
Keller

2
Ini bekerja dengan baik untuk saya. Untuk masalah saya adalah, lakukan permintaan jaringan, dan di dalam blok penyelesaian berhasil saya memanggil fungsi untuk memperbarui UI. Karena UIKit bukan utas aman, perlu mengirim kembali ke utas utama untuk memperbarui UI.
Rachel

1
Lihat jawaban oleh @Naishta untuk solusi Swift 3
Nathaniel

1
Untuk Swift 3: DispatchQueue.main.async () { code }, seperti kata
@Naishta
146

Anda mendapatkan pesan kesalahan yang sama saat melakukan debug dengan pernyataan cetak tanpa menggunakan 'dispatch_async' Jadi, ketika Anda mendapatkan pesan kesalahan itu, saatnya untuk menggunakan

Cepat 4

DispatchQueue.main.async { //code }

Cepat 3

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

Versi Swift sebelumnya

dispatch_async(dispatch_get_main_queue()){ //code }
Naishta
sumber
5
karena sintaksnya salah, seharusnya: dispatch_async(dispatch_get_main_queue(), ^{ /* UI related code */ });Edit: Saya memperbarui jawabannya, format sintaks berfungsi lebih baik di sana.
Zoltán
1
atau, ketika di dalam penutupan, turun ke mainthread dari utas latar belakang menggunakan ini: self.performSelectorOnMainThread (Selector ("yourFunction:"), withObject: 'yourArray / yourObject', waitUntilDone: true)
Naishta
1
Tidak, tidak, lihat sintaksinya
Naishta
82

Kesalahan "aplikasi ini memodifikasi mesin autolayout dari utas latar" dicatat dalam konsol lama setelah masalah yang sebenarnya terjadi, jadi debugging ini bisa sulit tanpa menggunakan breakpoint.

Saya menggunakan jawaban @ markussvensson untuk mendeteksi masalah saya dan menemukannya menggunakan Symbolic Breakpoint ini (Debug> Breakpoints> Create Symbolic Breakpoint):

  1. Simbol: [UIView layoutIfNeeded]atau[UIView updateConstraintsIfNeeded]
  2. Kondisi: !(BOOL)[NSThread isMainThread]

masukkan deskripsi gambar di sini

Bangun dan jalankan aplikasi pada emulator dan ulangi langkah-langkah yang menyebabkan pesan kesalahan dilemparkan (aplikasi akan lebih lambat dari biasanya!). Xcode kemudian akan menghentikan aplikasi dan menandai baris kode (mis. Panggilan func) yang mengakses UI dari utas latar belakang.

k06a
sumber
3
Hmm. Saya membenarkan ini, karena sepertinya masuk akal. Namun, eksekusi tidak rusak dan saya masih mendapatkan kesalahan di log saya. Apakah ada kondisi lain yang bisa saya gunakan untuk menetapkan breakpoint simbolis?
Sjakelien
Dalam kasus saya, itu rusak. Namun jejak stack tidak memberi saya petunjuk mana pun yang bertanggung jawab. Dan saya tidak tahu bagaimana menafsirkan kode assembler yang ditampilkanmovq 0x10880ba(%rip), %rsi ; "_wantsReapplicationOfAutoLayoutWithLayoutDirtyOnEntry:"
Reinhard Männer
Tidakkah hal itu menyebabkan aplikasi berjalan lambat dengan serius saat melakukan debug?
Itachi
@ Itachi ya. Tidak tahu bagaimana cara mempercepat.
k06a
2
Dimulai dengan Xcode 9 ini adalah fitur bawaan. Pastikan saja opsi "Pemeriksa Utas Utama" diaktifkan di tab "Diagnostics" pada pengaturan Skema
AndrewPo
24

Saat Anda mencoba memperbarui nilai bidang teks atau menambahkan subview di dalam utas latar belakang, Anda bisa mendapatkan masalah ini. Untuk alasan itu, Anda harus meletakkan kode semacam ini di utas utama.

Anda perlu membungkus metode yang memanggil pembaruan UI dengan dispatch_asynch untuk mendapatkan antrian utama. Sebagai contoh:

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

Diedit - SWIFT 3:

Sekarang, kita bisa melakukannya dengan mengikuti kode berikut:

// 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"
   }
}
Jorge Luis Jiménez
sumber
23

Bagi saya, pesan kesalahan ini berasal dari spanduk dari Admob SDK.

Saya dapat melacak asal ke "WebThread" dengan menetapkan breakpoint bersyarat.

breakpoint bersyarat untuk menemukan siapa yang memperbarui ui dari utas latar belakang

Kemudian saya bisa menyingkirkan masalah ini dengan merangkum pembuatan Banner dengan:

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

Saya tidak tahu mengapa ini membantu karena saya tidak dapat melihat bagaimana kode ini dipanggil dari non-main-thread.

Semoga bisa membantu siapa saja.

markussvensson
sumber
1
Saya memiliki masalah yang sama. Saya bingung mengapa pengecualian itu terjadi di WebThread. Saya membuat perubahan yang sama seperti yang Anda lakukan dan sekarang berfungsi. Saya menggunakan versi yang agak ketinggalan zaman dari admob SDK. Saya ingin tahu apakah itu diperbaiki dalam versi terbaru. Terima kasih untuk ini. Saya pikir saya tidak akan menemukannya.
Larry
1
Memperbarui ke versi terbaru AdMob memecahkan masalah ini untuk saya
JH95
Saya mendapatkan istirahat di breakpoint simbolis seperti ini, tetapi tidak kode ditampilkan. :(
Victor Engel
20

Saya punya masalah ini sejak memperbarui ke iOS 9 SDK ketika saya memanggil blok yang melakukan pembaruan UI dalam NSURLConnection async penangan penyelesaian permintaan. Menempatkan panggilan blok di dispatch_async menggunakan dispatch_main_queue memecahkan masalah.

Ini bekerja dengan baik di iOS 8.

spongessuck
sumber
10

Punya masalah yang sama karena saya menggunakan performSelectorInBackground.

Polisi
sumber
Tidak, saya perlu melakukan hal-hal di latar belakang. Saya meletakkan panggilan NSNotificationCenter di dalam metode dispatch_async (dispatch_get_main_queue () dan berhasil
Bobby
Saya mendapatkan data dari URLSessionDelegate, yang kemudian disebut NSNotification a UIViewController menanggapi pemberitahuan dan di sana saya menggunakan DispatchQueue.main.async untuk menunjukkan kepada pengguna apakah datanya baik atau tidak. Salah! - Solusinya adalah meletakkan Notifikasi di antrian utama. DispatchQueue.main.async {NotificationCenter.default.post (nama: NSNotification.Name (rawValue: networkNotificationNames.products.rawValue), objek: self, userInfo: [networkNotificationNames.products.rawValue: productList])}
iCyberPaul
7

Anda tidak harus mengubah UI dari utas utama! UIKit bukan utas yang aman, sehingga masalah di atas dan juga beberapa masalah aneh lainnya akan muncul jika Anda melakukannya. Aplikasi ini bahkan dapat macet.

Jadi, untuk melakukan operasi UIKit, Anda perlu mendefinisikan blok dan membiarkannya dieksekusi pada antrian utama: seperti,

NSOperationQueue.mainQueue().addOperationWithBlock {

}
Chowdhury Md Rajib Sarwar
sumber
Ini tidak tersedia di Xcode 7.2 dan iOS 9.2. Ada alternatif lain?
Jayprakash Dubey
7

Jelas Anda sedang melakukan beberapa pembaruan UI di back ground thread. Tidak dapat memprediksi dengan tepat di mana, tanpa melihat kode Anda.

Ini adalah beberapa situasi yang mungkin terjadi: -

Anda mungkin melakukan sesuatu di utas latar belakang dan tidak menggunakan. Berfungsi sama dengan kode ini lebih mudah dikenali.

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

memanggil func melakukan panggilan permintaan web pada utas latar belakang dan penangannya selesai memanggil func lainnya melakukan pembaruan ui. untuk menyelesaikan ini coba periksa kode di mana Anda telah memperbarui UI setelah panggilan permintaan web.

// 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()
            }
}
Pisey Ashish
sumber
5

Masalah utama dengan "Aplikasi ini memodifikasi mesin autolayout dari utas latar belakang" adalah bahwa tampaknya akan dicatat lama setelah masalah yang sebenarnya terjadi, ini dapat membuatnya sangat sulit untuk memecahkan masalah.

Saya berhasil memecahkan masalah ini dengan membuat tiga breakpoint simbolis.

Debug> Breakpoints> Buat Sympointic Breakpoint ...

Breakpoint 1:

  • Simbol: -[UIView setNeedsLayout]

  • Kondisi: !(BOOL)[NSThread isMainThread]

Breakpoint 2:

  • Simbol: -[UIView layoutIfNeeded]

  • Kondisi: !(BOOL)[NSThread isMainThread]

Breakpoint 3:

  • Simbol: -[UIView updateConstraintsIfNeeded]

  • Kondisi: !(BOOL)[NSThread isMainThread]

Dengan breakpoints ini, Anda dapat dengan mudah mendapatkan jeda pada baris aktual di mana Anda salah memanggil metode UI pada utas non-utama.

www.jensolsson.se
sumber
4

Saya mengalami masalah ini saat memuat ulang data di UITableView. Cukup kirim ulang isi sebagai berikut perbaiki masalah untuk saya.

    dispatch_async(dispatch_get_main_queue(), { () -> Void in
        self.tableView.reloadData()
    })
ReshDev
sumber
Ini untukku! Saya memiliki semuanya di antrian utama tetapi reloadData ()!
Oprimus
3

Saya memiliki masalah yang sama. Ternyata saya menggunakan UIAlertsyang membutuhkan antrian utama. Tapi, mereka sudah usang .
Ketika saya mengubah UIAlertske UIAlertController, saya tidak lagi memiliki masalah dan tidak perlu menggunakan dispatch_asynckode apa pun . Pelajaran - memperhatikan peringatan. Mereka membantu bahkan ketika Anda tidak mengharapkannya.

epaus
sumber
3

Anda sudah memiliki jawaban kode yang benar dari @ Mark tetapi, hanya untuk membagikan temuan saya: Masalahnya adalah Anda meminta perubahan dalam tampilan dan mengasumsikan bahwa itu akan terjadi secara instan. Pada kenyataannya, pemuatan tampilan tergantung pada sumber daya yang tersedia. Jika semuanya memuat cukup cepat dan tidak ada penundaan maka Anda tidak melihat apa pun. Dalam skenario, di mana ada keterlambatan karena utas proses sedang sibuk dll, aplikasi mengalami situasi di mana ia seharusnya menampilkan sesuatu meskipun belum siap. Oleh karena itu, disarankan untuk mengirimkan permintaan ini dalam antrian asinkron sehingga, mereka dieksekusi berdasarkan beban.

Mukund Agarwal
sumber
2

Saya mengalami masalah ini ketika saya menggunakan TouchID jika itu membantu orang lain, bungkus logika kesuksesan Anda yang kemungkinan melakukan sesuatu dengan UI di antrian utama.

Stuart P.
sumber
2

Ini bisa berupa sesuatu yang sederhana seperti menyetel bidang teks / nilai label atau menambahkan subview di dalam utas latar belakang, yang dapat menyebabkan tata letak bidang berubah. Pastikan apa pun yang Anda lakukan dengan antarmuka hanya terjadi di utas utama.

Periksa tautan ini: https://forums.developer.apple.com/thread/7399

Kiran P Nair
sumber
2

Saya memiliki masalah yang sama ketika mencoba memperbarui pesan kesalahan di UILabel di ViewController yang sama (butuh beberapa saat untuk memperbarui data ketika mencoba melakukan itu dengan pengkodean normal). Saya menggunakan DispatchQueueSwift 3 Xcode 8 dan berfungsi.

FN90
sumber
2

Jika Anda ingin mencari kesalahan ini, gunakan pemeriksa utas jeda pada masalah centang. Memperbaiki itu mudah sebagian besar kali, mengirim garis bermasalah di antrian utama.

masukkan deskripsi gambar di sini

rockdaswift
sumber
1
Apa sebenarnya yang dilakukan ini? "Main Thread Checker" diaktifkan secara default dan saya juga memeriksa "Thread Sanitizer" dan "Jeda pada masalah" untuk keduanya tetapi masih hanya melempar pesan "memodifikasi dari utas latar" tanpa menambahkan informasi lebih lanjut.
Neph
Ini menjeda eksekusi aplikasi di titik di mana UI sedang dimodifikasi di utas latar belakang.
rockdaswift
Aneh, itu tidak melakukan itu di aplikasi saya (Xcode 10.2.1). Saya harus menambahkan breakpoint secara manual (seperti dijelaskan di sini ) untuk menghentikannya dan mengarahkan saya ke baris kode.
Neph
Dan apa yang saya lakukan untuk melihat set opsi ini?
David Rektor
Edit skema target Anda
rockdaswift
1

Bagi saya masalahnya adalah sebagai berikut. Pastikan performSegueWithIdentifier:dilakukan di utas utama:

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

Swift 4,

Misalkan, jika Anda memanggil beberapa metode menggunakan operasi antrian

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

Dan misalkan fungsi searchFavourites seperti,

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

jika Anda memanggil, semua kode di dalam metode "searchFavourites" pada utas utama, itu masih akan memberikan kesalahan jika Anda memperbarui beberapa UI di dalamnya.

Aplikasi ini memodifikasi mesin autolayout dari utas latar setelah mesin diakses dari utas utama.

Jadi gunakan solusi,

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

Untuk skenario seperti ini.

Pramod More
sumber
1

Di sini, periksa baris ini dari log

$S12AppName18ViewControllerC11Func()ySS_S2StF + 4420

Anda dapat memeriksa fungsi mana yang memanggil dari utas latar belakang atau tempat Anda memanggil metode api, Anda harus memanggil fungsi Anda dari utas utama seperti ini.

DispatchQueue.main.async { func()}

func () adalah fungsi yang ingin Anda panggil dalam hasil panggilan api sukses atau yang lain.

Log di Sini

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
)
Govind Wadhwa
sumber
0

Saya juga mengalami masalah ini, melihat satu ton pesan ini dan jejak stack sedang dicetak dalam output, ketika saya mengubah ukuran jendela ke ukuran yang lebih kecil dari nilai awalnya. Menghabiskan waktu yang lama untuk mencari tahu masalahnya, saya pikir saya akan membagikan solusi yang agak sederhana. Saya pernah diaktifkan Can Draw Concurrentlypada NSTextViewmelalui IB. Itu memberitahu AppKit bahwa ia dapat memanggil metode tampilan draw(_:)dari utas lain. Setelah menonaktifkannya, saya tidak lagi mendapat pesan kesalahan. Saya tidak mengalami masalah sebelum memperbarui ke macOS 10.14 Beta, tetapi pada saat yang sama, saya juga mulai memodifikasi kode untuk melakukan pekerjaan dengan tampilan teks.

Andreas
sumber