ChatGPT解决这个技术问题 Extra ChatGPT

Getting a "This application is modifying the autolayout engine from a background thread" error?

Been encountering this error a lot in my OS X using swift:

"This application is modifying the autolayout engine from a background thread, which can lead to engine corruption and weird crashes. This will cause an exception in a future release."

I have a my NSWindow and I'm swapping in views to the contentView of the window. I get the error when I try and do a NSApp.beginSheet on the window, or when I add a subview to the window. Tried disabling autoresize stuff, and I don't have anything using auto layout. Any thoughts?

Sometimes it's fine and nothing happens, other times it totally breaks my UI and nothing loads

For some reason an excellent answer below was deleted: github.com/nrbrook/NBUIKitMainThreadGuard
Saved me couple of hours at least. Thanks @Fattie
right @oyalhi. be careful using it, I really enjoyed it but then had other problems too - it's a tough field! hope it helps!
a semi related question

p
pkamb

It needs to be placed inside a different thread that allows the UI to update as soon as execution of thread function completes:

Modern Swift:

DispatchQueue.main.async {
    // Update UI
}

Older versions of Swift, pre Swift 3.

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

Objective-C:

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

To make this work in Objective C, put ^(void) before the { in the code block, and a semicolon afterwords.
While it doesn't hurt, ^(void) instead of just ^ is not necessary. The Objective-C version of the answer is just fine.
Is there a way to debug in Xcode which line is causing the error?
It works fine for me. For my problem is, do a network request, and inside success completion block i called function to update UI. Since UIKit is not thread safe, need to dispatch back to main thread to update UI.
See answer by @Naishta for Swift 3 solution
N
Naishta

You get similar error message while debugging with print statements without using 'dispatch_async' So when you get that error message, its time to use

Swift 4

DispatchQueue.main.async { //code }

Swift 3

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

Earlier Swift versions

dispatch_async(dispatch_get_main_queue()){ //code }

because the syntax is wrong, should be: dispatch_async(dispatch_get_main_queue(), ^{ /* UI related code */ }); Edit: I updated his answer, syntax formatting works better there.
or , when inside a closure, get off to the mainthread from the background thread using this : self.performSelectorOnMainThread(Selector("yourFunction:"), withObject:'yourArray/yourObject', waitUntilDone: true)
No its not, have a look at the syntax
N
Neph

The "this application is modifying the autolayout engine from a background thread" error is logged in the console long after the actual problem occured, so debugging this can be hard without using a breakpoint.

I used @markussvensson's answer to detect my problem and found it using this Symbolic Breakpoint (Debug > Breakpoints > Create Symbolic Breakpoint):

Symbols: [UIView layoutIfNeeded] or [UIView updateConstraintsIfNeeded] Condition: !(BOOL)[NSThread isMainThread]

https://i.stack.imgur.com/Pw1q5.png

Build and run the app on the emulator and replicate the steps that lead to the error message being thrown (the app will be slower than usual!). Xcode will then stop the app and mark the line of code (e.g. the call of a func) that's accessing the UI from a background thread.


Hmm. I upvoted this, because it seemed to make sense. However, the execution does not break and I still get the error in my logs. Are there other conditions I could use to set a symbolic breakpoint?
In my case, it does break. However the stack trace does not give me any hint which view is responsible. And I don't know how to interpret the shown assembler code movq 0x10880ba(%rip), %rsi ; "_wantsReapplicationOfAutoLayoutWithLayoutDirtyOnEntry:"
Won't it cause the app run slow seriously when debugging?
Starting with Xcode 9 it's a built-in feature. Just make sure the "Main Thread Checker" option is enabled in "Diagnostics" tab of Scheme settings
@AndrewPo In my Xcode 10 "Main Thread Checker" is enabled by default but it only output the error message and since those are output a while after the problem happened, it didn't help. I had to add a breakpoint to find the right spot. Btw, this question suggests adding a third breakpoint for -[UIView setNeedsLayout] too (sorry, no idea what it does exactly), so maybe if it didn't break, that one was missing.
V
Varun Naharia

When you try to update a text field value or adding a subview inside a background thread, you can get this problem. For that reason, you should put this kind of code in the main thread.

You need to wrap methods that call UI updates with dispatch_asynch to get the main queue. For example:

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

EDITED - SWIFT 3:

Now, we can do that following the next code:

// 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"
   }
}

m
markussvensson

For me, this error message originated from a banner from Admob SDK.

I was able to track the origin to "WebThread" by setting a conditional breakpoint.

https://i.stack.imgur.com/bYqev.png

Then I was able to get rid of the issue by encapsulating the Banner creation with:

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

I don't know why this helped as I cannot see how this code was called from a non-main-thread.

Hope it can help anyone.


I had the same problem. I was confused why the exception was occurring in a WebThread. I made the same change as you did and it now works. I am using a slightly out of date version of the admob sdk. I wonder if it was corrected in the latest version. Thanks for this. I don't think I would have found it.
Updating to the latest version of AdMob solved this issue for me
I get a break at a symbolic breakpoint like this, but not code is shown. :(
s
spongessuck

I had this problem since updating to the iOS 9 SDK when I was calling a block that did UI updates within an NSURLConnection async request completion handler. Putting the block call in a dispatch_async using dispatch_main_queue solved the issue.

It worked fine in iOS 8.


G
Glen Selle

Had the same problem because I was using performSelectorInBackground.


No, I needed to do things in the background. I put the NSNotificationCenter call inside the dispatch_async(dispatch_get_main_queue() method and it worked.
I was getting data from a URLSessionDelegate, which then called a NSNotification a UIViewController responded to the notification and in there I used DispatchQueue.main.async to show the user if the data was good or not. Wrong! - The solution is put the Notification in the main queue. DispatchQueue.main.async { NotificationCenter.default.post(name: NSNotification.Name(rawValue: networkNotificationNames.products.rawValue), object: self, userInfo: [networkNotificationNames.products.rawValue:productList]) }
w
www.jensolsson.se

Main problem with "This application is modifying the autolayout engine from a background thread" is that it seem to be logged a long time after the actual problem occurs, this can make it very hard to troubleshoot.

I managed to solve the issue by creating three symbolic breakpoints.

Debug > Breakpoints > Create Symbolic Breakpoint...

Breakpoint 1:

Symbol: -[UIView setNeedsLayout]

Condition: !(BOOL)[NSThread isMainThread]

Breakpoint 2:

Symbol: -[UIView layoutIfNeeded]

Condition: !(BOOL)[NSThread isMainThread]

Breakpoint 3:

Symbol: -[UIView updateConstraintsIfNeeded]

Condition: !(BOOL)[NSThread isMainThread]

With these breakpoints, you can easily get a break on the actual line where you incorrectly call UI methods on non-main thread.


C
Chowdhury Md Rajib Sarwar

You must not change the UI offside the main thread! UIKit is not thread safe, so that above problem and also some other weird problems will arise if you do that. The app can even crash.

So, to do the UIKit operations, you need to define block and let it be executed on the main queue: such as,

NSOperationQueue.mainQueue().addOperationWithBlock {

}

This is not available in Xcode 7.2 and iOS 9.2. Any other alternative?
A
Ashish Pisey

Obviously you are doing some UI update on back ground thread. Cant predict exactly where, without seeing your code.

These are some situations it might happen:-

you might be doing something on background thread and not using. Being in the same function this code is easier to spot.

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

calling a func doing web request call on background thread and its completion handler calling other func doing ui update. to solve this try checking code where you have updated the UI after webrequest call.

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

R
ReshDev

I had this issue while reloading data in UITableView. Simply dispatching reload as follows fixed the issue for me.

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

This was it for me! I had everything else on main queue but a stray reloadData()!
ρ
ρяσѕρєя K

I had the same problem. Turns out I was using UIAlerts that needed the main queue. But, they've been deprecated.
When I changed the UIAlerts to the UIAlertController, I no longer had the problem and did not have to use any dispatch_async code. The lesson - pay attention to warnings. They help even when you don't expect it.


M
Mukund Agarwal

You already have the correct code answer from @Mark but, just to share my findings: The issue is that you are requesting a change in the view and assuming that it will happen instantly. In reality, the loading of a view depends on the available resources. If everything loads quickly enough and there are no delays then you don't notice anything. In scenarios, where there is any delay due to the process thread being busy etc, the application runs into a situation where it is supposed to display something even though its not ready yet. Hence, it is advisable to dispatch these requests in a asynchronous queues so, they get executed based on the load.


S
Stu P.

I had this issue when I was using TouchID if that helps anyone else, wrap your success logic which likely does something with the UI in the main queue.


K
Kiran P Nair

It could be something as simple as setting a text field / label value or adding a subview inside a background thread, which may cause a field's layout to change. Make sure anything you do with the interface only happens in the main thread.

Check this link: https://forums.developer.apple.com/thread/7399


C
CrazyTN

I had the same issue when trying to update error message in UILabel in the same ViewController (it takes a little while to update data when trying to do that with normal coding). I used DispatchQueue in Swift 3 Xcode 8 and it works.


r
rockdaswift

If you want to hunt this error, use the main thread checker pause on issues checkbox. Fixing it is easy most of the times, dispatching the problematic line in the main queue.

https://i.stack.imgur.com/wK4BS.png


What does this exactly do? "Main Thread Checker" was enabled by default and I additionally checked "Thread Sanitizer" and "Pause on issues" for both but it still only throws the "modifying from a background thread" message without adding any more information to it.
It pauses the execution of the app in the point where the UI is being modified in a background thread.
Weird, it didn't do that in my app (Xcode 10.2.1). I had to manually add breakpoints (as described here) to get it to pause and point me at the line of code.
And what do I do to see this set of options?
Edit the scheme of your target
O
Offek

For me the issue was the following. Make sure performSegueWithIdentifier: is performed on the main thread:

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

P
Pramod More

Swift 4,

Suppose, if you are calling some method using operation queue

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

And suppose function searchFavourites is like,

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

if you call, all code inside the method "searchFavourites" on the main thread, it will still give an error if you are updating some UI in it.

This application is modifying the autolayout engine from a background thread after the engine was accessed from the main thread.

So use solution,

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

For this kind of scenario.


G
Govind Wadhwa

Here check out this line from the logs

$S12AppName18ViewControllerC11Func()ySS_S2StF + 4420

you can check that which function calling from the either background thread or where you are calling api method you need to call your function from the main thread like this.

DispatchQueue.main.async { func()}

func() is that function you want to call in the result of api call success or else.

Logs Here

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
)

A
Andreas detests censorship

I also encountered this problem, seeing a ton of these messages and stack traces being printed in the output, when I resized the window to a smaller size than its initial value. Spending a long time figuring out the problem, I thought I'd share the rather simple solution. I had once enabled Can Draw Concurrently on an NSTextView through IB. That tells AppKit that it can call the view's draw(_:) method from another thread. After disabling it, I no longer got any error messages. I didn't experience any problems before updating to macOS 10.14 Beta, but at the same time, I also started modifying the code to perform work with the text view.


关注公众号,不定期副业成功案例分享
Follow WeChat

Success story sharing

Want to stay one step ahead of the latest teleworks?

Subscribe Now