【问题标题】:Getting a "This application is modifying the autolayout engine from a background thread" error?收到“此应用程序正在从后台线程修改自动布局引擎”错误?
【发布时间】:2015-02-03 15:01:18
【问题描述】:

在我的 OS X 中使用 swift 经常遇到这个错误:

“此应用程序正在从后台线程修改自动布局引擎,这可能导致引擎损坏和奇怪的崩溃。这将在未来的版本中导致异常。”

我有一个我的 NSWindow,我正在将视图切换到窗口的 contentView。当我尝试在窗口上执行NSApp.beginSheet 或将subview 添加到窗口时,我得到错误。尝试禁用自动调整大小的东西,我没有任何使用自动布局的东西。有什么想法吗?

有时它很好,什么也没有发生,有时它完全破坏了我的 UI 并且没有加载

【问题讨论】:

  • 由于某种原因,下面的一个很好的答案被删除了:github.com/nrbrook/NBUIKitMainThreadGuard
  • 至少为我节省了几个小时。谢谢@Fattie
  • 对@oyalhi。小心使用它,我真的很喜欢它,但也遇到了其他问题——这是一个艰难的领域!希望有帮助!
  • 一个semi related 问题

标签: swift macos autolayout background-thread


【解决方案1】:

它需要放置在一个不同的线程中,以便 UI 在线程函数执行完成后立即更新:

现代斯威夫特:

DispatchQueue.main.async {
    // Update UI
}

Swift 3 之前的旧版本。

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

目标-C:

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

【讨论】:

  • 要在 Objective C 中实现这一点,请将 ^(void) 放在代码块中的 { 之前,并在后面加一个分号。
  • 虽然没有伤害,但 ^(void) 而不是 ^ 是不必要的。答案的 Objective-C 版本就好了。
  • 有没有办法在 Xcode 中调试导致错误的行?
  • 它对我来说很好用。对于我的问题是,做一个网络请求,并在成功完成块内我调用函数来更新 UI。由于 UIKit 不是线程安全的,需要调度回主线程来更新 UI。
  • 查看@Naishta 对 Swift 3 解决方案的回答
【解决方案2】:

使用打印语句进行调试而不使用“dispatch_async”时,您会收到类似的错误消息 因此,当您收到该错误消息时,是时候使用了

斯威夫特 4

DispatchQueue.main.async { //code }

斯威夫特 3

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

早期的 Swift 版本

dispatch_async(dispatch_get_main_queue()){ //code }

【讨论】:

  • 因为语法错误,应该是:dispatch_async(dispatch_get_main_queue(), ^{ /* UI related code */ }); 编辑:我更新了他的答案,语法格式在那里效果更好。
  • 或者,当在一个闭包中时,使用这个从后台线程下车到主线程: self.performSelectorOnMainThread(Selector("yourFunction:"), withObject:'yourArray/yourObject', waitUntilDone: true )
  • 不,不是,看看语法
【解决方案3】:

“此应用程序正在从后台线程修改自动布局引擎”错误会在实际问题发生很久之后记录在控制台中,因此如果不使用断点进行调试可能会很困难。

我使用@markussvensson 的答案来检测我的问题并使用此符号断点(调试> 断点> 创建符号断点)找到它:

  1. 符号:[UIView layoutIfNeeded][UIView updateConstraintsIfNeeded]
  2. 条件:!(BOOL)[NSThread isMainThread]

在模拟器上构建并运行应用,并复制导致抛出错误消息的步骤(应用会比平时慢!)。然后 Xcode 将停止应用程序并标记从后台线程访问 UI 的代码行(例如 func 的调用)。

【讨论】:

  • 嗯。我赞成这一点,因为它似乎有道理。但是,执行没有中断,我的日志中仍然出现错误。还有其他条件可以用来设置符号断点吗?
  • 就我而言,它确实坏了。但是堆栈跟踪并没有给我任何提示哪个视图负责。而且我不知道如何解释显示的汇编代码movq 0x10880ba(%rip), %rsi ; "_wantsReapplicationOfAutoLayoutWithLayoutDirtyOnEntry:"
  • 调试时会不会严重导致app运行缓慢?
  • 从 Xcode 9 开始,它是一个内置功能。只需确保在方案设置的“诊断”选项卡中启用了“主线程检查器”选项
  • @AndrewPo 在我的 Xcode 10 中,“主线程检查器”默认启用,但它只输出错误消息,因为这些是在问题发生后一段时间才输出的,所以没有帮助。我必须添加一个断点才能找到正确的位置。顺便说一句,this 问题建议也为-[UIView setNeedsLayout] 添加第三个断点(抱歉,不知道它到底做了什么),所以如果它没有中断,那可能就丢失了。
【解决方案4】:

当您尝试更新文本字段值或在后台线程中添加子视图时,您可能会遇到此问题。因此,您应该将这种代码放在主线程中。

您需要使用 dispatch_asynch 包装调用 UI 更新的方法以获取主队列。例如:

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

【讨论】:

    【解决方案5】:

    对我来说,此错误消息源自 Admob SDK 的横幅。

    我能够通过设置条件断点来跟踪“WebThread”的来源。

    然后我能够通过封装横幅创建来摆脱这个问题:

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

    我不知道这有什么帮助,因为我看不到这段代码是如何从非主线程调用的。

    希望它可以帮助任何人。

    【讨论】:

    • 我遇到了同样的问题。我很困惑为什么异常发生在 WebThread 中。我做了和你一样的改变,现在可以了。我正在使用稍微过时的 admob sdk 版本。我想知道它是否在最新版本中得到纠正。谢谢你。我不认为我会找到它。
    • 更新到最新版本的 AdMob 为我解决了这个问题
    • 我在这样的符号断点处中断,但没有显示代码。 :(
    【解决方案6】:

    当我在 NSURLConnection 异步请求完成处理程序中调用一个执行 UI 更新的块时,我在更新到 iOS 9 SDK 后遇到了这个问题。使用 dispatch_main_queue 将块调用放入 dispatch_async 中解决了这个问题。

    它在 iOS 8 中运行良好。

    【讨论】:

      【解决方案7】:

      遇到同样的问题,因为我使用的是performSelectorInBackground

      【讨论】:

      • 不,我需要在后台做事。我将 NSNotificationCenter 调用放在 dispatch_async(dispatch_get_main_queue() 方法中,它起作用了。
      • 我从 URLSessionDelegate 获取数据,然后调用 NSNotification 一个 UIViewController 响应通知,在那里我使用 DispatchQueue.main.async 向用户显示数据是否良好。错误的! - 解决方案是将通知放在主队列中。 DispatchQueue.main.async { NotificationCenter.default.post(name: NSNotification.Name(rawValue: networkNotificationNames.products.rawValue), object: self, userInfo: [networkNotificationNames.products.rawValue:productList]) }
      【解决方案8】:

      “此应用程序正在从后台线程修改自动布局引擎”的主要问题是,它似乎在实际问题发生后很长时间才被记录,这使得故障排除非常困难。

      我通过创建三个符号断点设法解决了这个问题。

      调试 > 断点 > 创建符号断点...

      断点 1:

      • 符号:-[UIView setNeedsLayout]

      • 条件:!(BOOL)[NSThread isMainThread]

      断点 2:

      • 符号:-[UIView layoutIfNeeded]

      • 条件:!(BOOL)[NSThread isMainThread]

      断点 3:

      • 符号:-[UIView updateConstraintsIfNeeded]

      • 条件:!(BOOL)[NSThread isMainThread]

      使用这些断点,您可以轻松地在您错误地调用非主线程上的 UI 方法的实际行中中断。

      【讨论】:

        【解决方案9】:

        您不得在主线程之外更改 UI! UIKit 不是线程安全的,所以如果你这样做,就会出现上述问题以及其他一些奇怪的问题。应用甚至可能崩溃。

        所以,要进行 UIKit 操作,你需要定义 block 并让它在主队列上执行:例如,

        NSOperationQueue.mainQueue().addOperationWithBlock {
        
        }
        

        【讨论】:

        • 这在 Xcode 7.2 和 iOS 9.2 中不可用。还有其他选择吗?
        【解决方案10】:

        显然,您正在后台线程上进行一些 UI 更新。没有看到你的代码就无法准确预测在哪里。

        以下是它可能发生的一些情况:-

        您可能正在后台线程上做某事而没有使用。在同一个函数中,这段代码更容易被发现。

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

        在后台线程上调用一个 func 执行 Web 请求调用,并且它的完成处理程序调用其他 func 执行 ui 更新。 要解决此问题,请尝试在 webrequest 调用后检查您更新 UI 的代码。

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

        【讨论】:

          【解决方案11】:

          我在 UITableView 中重新加载数据时遇到了这个问题。只需按如下方式分派重新加载即可解决我的问题。

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

          【讨论】:

          • 这是给我的!我在主队列上有其他所有东西,但有一个流浪的 reloadData()!
          【解决方案12】:

          我遇到了同样的问题。原来我使用的是需要主队列的UIAlerts。但是,它们已被弃用
          当我将UIAlerts 更改为UIAlertController 时,我不再遇到问题并且不必使用任何dispatch_async 代码。教训 - 注意警告。即使您没有预料到,它们也会提供帮助。

          【讨论】:

            【解决方案13】:

            您已经从@Mark 获得了正确的代码答案,但只是为了分享我的发现: 问题是您请求更改视图并假设它会立即发生。实际上,视图的加载取决于可用资源。如果一切都加载得足够快并且没有延迟,那么您就不会注意到任何事情。在由于进程线程繁忙等导致延迟的情况下,应用程序会遇到即使尚未准备好也应该显示某些内容的情况。因此,建议在异步队列中分派这些请求,以便根据负载执行它们。

            【讨论】:

              【解决方案14】:

              我在使用 TouchID 时遇到了这个问题,如果这对其他人有帮助,请将您的成功逻辑包装起来,该逻辑可能对主队列中的 UI 有所帮助。

              【讨论】:

                【解决方案15】:

                这可能很简单,例如设置文本字段/标签值或在后台线程中添加子视图,这可能会导致字段的布局发生变化。确保您对界面所做的任何事情都只发生在主线程中。

                查看此链接:https://forums.developer.apple.com/thread/7399

                【讨论】:

                  【解决方案16】:

                  尝试在同一个 ViewController 中更新 UILabel 中的错误消息时,我遇到了同样的问题(尝试使用普通编码来更新数据需要一点时间)。我在 Swift 3 Xcode 8 中使用了DispatchQueue,它可以工作。

                  【讨论】:

                    【解决方案17】:

                    如果您想查找此错误,请使用主线程检查器暂停问题复选框。 大多数时候修复它很容易,在主队列中调度有问题的行。

                    【讨论】:

                    • 这到底是做什么的? “主线程检查器”默认启用,我另外检查了“线程清理器”和“问题暂停”,但它仍然只抛出“从后台线程修改”消息,而不向其添加任何更多信息。
                    • 它会在后台线程中修改 UI 时暂停应用程序的执行。
                    • 奇怪,在我的应用程序(Xcode 10.2.1)中没有这样做。我必须手动添加断点(如here 所述)以使其暂停并将我指向代码行。
                    • 我该怎么做才能看到这组选项?
                    • 编辑你的目标方案
                    【解决方案18】:

                    对我来说,问题如下。 确保在主线程上执行performSegueWithIdentifier:

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

                    【讨论】:

                      【解决方案19】:

                      斯威夫特 4,

                      假设,如果你正在使用操作队列调用某个方法

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

                      假设函数 searchFavourites 是这样的,

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

                      如果您在主线程上调用方法“searchFavourites”中的所有代码,如果您在其中更新某些 UI,它仍然会出错。

                      此应用程序正在从后台修改自动布局引擎 从主线程访问引擎后的线程。

                      所以使用解决方案,

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

                      对于这种情况。

                      【讨论】:

                        【解决方案20】:

                        在这里查看日志中的这一行

                        $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
                        )
                        

                        【讨论】:

                          【解决方案21】:

                          我也遇到了这个问题,当我将窗口的大小调整为小于其初始值时,输出中显示了大量的这些消息和堆栈跟踪。花了很长时间解决这个问题,我想我会分享一个相当简单的解决方案。我曾经通过 IB 在NSTextView 上启用了Can Draw Concurrently。这告诉 AppKit 它可以从另一个线程调用视图的draw(_:) 方法。禁用它后,我不再收到任何错误消息。在更新到 macOS 10.14 Beta 之前我没有遇到任何问题,但同时我也开始修改代码以执行文本视图的工作。

                          【讨论】:

                            猜你喜欢
                            • 1970-01-01
                            • 1970-01-01
                            • 2017-05-14
                            • 1970-01-01
                            • 2016-06-20
                            • 2018-01-23
                            • 1970-01-01
                            • 1970-01-01
                            • 1970-01-01
                            相关资源
                            最近更新 更多