主线程检查器:在后台线程上调用的 UI API:-[UIApplication applicationState]

Posted

技术标签:

【中文标题】主线程检查器:在后台线程上调用的 UI API:-[UIApplication applicationState]【英文标题】:Main Thread Checker: UI API called on a background thread: -[UIApplication applicationState] 【发布时间】:2017-11-29 18:27:56 【问题描述】:

我在 Xcode 9 beta、ios 11 中使用谷歌地图。

我收到如下错误输出到日志:

PID:4442,TID:837820,线程名称:com.google.Maps.LabelingBehavior,队列名称:com.apple.root.default-qos.overcommit,QoS:21

为什么会发生这种情况,因为我几乎可以肯定我没有更改代码中主线程的任何界面元素。

 override func viewDidLoad() 

    let locationManager = CLLocationManager()


    locationManager.requestAlwaysAuthorization()


    locationManager.requestWhenInUseAuthorization()

        if CLLocationManager.locationServicesEnabled() 

            locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
            locationManager.startUpdatingLocation()
        

      viewMap.delegate = self

     let camera = GMSCameraPosition.camera(withLatitude: 53.7931183329367, longitude: -1.53649874031544, zoom: 17.0)


        viewMap.animate(to: camera)


    

    func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) 
        let locValue:CLLocationCoordinate2D = manager.location!.coordinate
        print("locations = \(locValue.latitude) \(locValue.longitude)")
    

    func mapView(_ mapView: GMSMapView, willMove gesture: Bool) 


    

    func mapView(_ mapView: GMSMapView, idleAt position: GMSCameraPosition) 

        if(moving > 1)
            moving = 1
        UIView.animate(withDuration: 0.5, delay: 0, animations: 

            self.topBarConstraint.constant = self.topBarConstraint.constant + (self.topBar.bounds.height / 2)

            self.bottomHalfConstraint.constant = self.bottomHalfConstraint.constant + (self.topBar.bounds.height / 2)

            self.view.layoutIfNeeded()
        , completion: nil)
    
         moving = 1
    


    // Camera change Position this methods will call every time
    func mapView(_ mapView: GMSMapView, didChange position: GMSCameraPosition) 
        moving = moving + 1
        if(moving == 2)


            UIView.animate(withDuration: 0.5, delay: 0, animations: 


                self.topBarConstraint.constant = self.topBarConstraint.constant - (self.topBar.bounds.height / 2)

                self.bottomHalfConstraint.constant = self.bottomHalfConstraint.constant - (self.topBar.bounds.height / 2)


                self.view.layoutIfNeeded()
            , completion: nil)
        
        DispatchQueue.main.async 

            print("Moving: \(moving) Latitude: \(self.viewMap.camera.target.latitude)")
            print("Moving: \(moving)  Longitude: \(self.viewMap.camera.target.longitude)")
        
    

【问题讨论】:

mapView(_:didChange) 中,您将print 语句分派到主队列。你不是已经在主队列上了吗?如果没有,您也必须将animate 调用发送到主队列。我建议在这些 UI 更新之前插入一些 dispatchPrecondition(condition: .onQueue(.main)),以确保。 您说“我几乎可以肯定我不会从我的代码中的主线程更改任何界面元素。”我假设您的意思是“......来自任何后台线程。” 不是你的问题。我认为这是在他们的尽头。它停在“com.google.Maps.LabelingBehavior”中。我有同样的问题。 您好,是的,问题似乎出在 google 上,希望他们能尽快发布更新版本 @MattBlack 看看这个答案:***.com/a/44392584/5912335 【参考方案1】:

有时很难找到不在主线程中执行的 UI 代码。您可以使用下面的技巧找到并修复它。

    选择 Edit Scheme -> Diagnostics,勾选 Main Thread Checker。

    Xcode 11.4.1

    单击主线程检查器旁边的小箭头以创建主线程检查器断点。

    以前的 Xcode

    勾选暂停问题。

    运行您的 iOS 应用程序以重现此问题。 (Xcode 应该在第一个问题上暂停。)

    将修改 UI 的代码封装在 DispatchQueue.main.async

【讨论】:

谢谢,也许可以节省我 30 分钟。 出于某种原因,当我在 Xcode 10.1 中这样做时,我只得到一个无助的调用堆栈,并且我无法与代码行相关联:2018-12-29 19:46:56.500629+0100 BedtimePrototype[1553:834478] [报告]主线程检查器:在后台线程上调用的 UI API:-[UIApplication applicationState] PID:1553,TID:834478,线程名称:com.apple.CoreMotion.MotionThread,队列名称:com。 apple.root.default-qos.overcommit,QoS:0 我也得到了一个看起来很无奈的调用栈。在左侧,它停在"com.apple.CoreMotion.MotionThread(23)",然后我也检查了所有其他线程。在Thread 1 中引起了我的注意:它是 SVProgressHUD(我使用的进度视图库)。所以,我知道这是对目标视图控制器某处的SVProgressHUD.show() 的调用。然后用DispatchQueue.main.async 包裹每个外观,或者简单地注释掉,再次测试,我发现哪个有问题。 我还通过注释掉 SVProgressHUD.show 摆脱了警告。将此调用包装到 DispatchQueue.main.async 并没有消除警告。我在 v2.2.5 中使用 pod 这个线程可以帮助更好地理解问题:github.com/SVProgressHUD/SVProgressHUD/issues/950 xcode 11.4.1 中没有“问题暂停”复选框。编辑:我在主线程检查器旁边发现了一个小箭头。单击它会为您添加一个断点。【参考方案2】:

首先,确保从主线程调用谷歌地图和用户界面更改。

您可以使用以下步骤在 Xcode 中启用Thread Sanitizer 选项:

您可以在主线程中添加以下内容:

DispatchQueue.main.async 
    //Do UI Code here. 
    //Call Google maps methods.

另外,更新您当前版本的谷歌地图。 Google 地图必须对线程检查器进行一些更新。

对于问题:“为什么会发生这种情况?”我认为 Apple 为边缘案例添加了 assertion,然后 Google 必须更新其 pod。

【讨论】:

@thibautnoah,您是说因为我没有将其表述为“它(问题)正在发生,因为您将 xcode 9 beta 与 Google 的 API 结合使用?”还是因为您面临我的回答无法解决的类似错误? Xcode 9 突出了一些 xcode 8 显然没有检测到的线程问题(无论是由于 xcode 设置还是其他有待确定的问题)。切换回 xcode 8 相当于忽略了你的线程问题,它们仍然存在并且没有解决,因此这不是一个解决方案,你只是把头埋在沙子里,假装一切正常。如果问题来自框架,请提交问题以便修复。 Xcode 9 检测到 xcode 8 无法检测到的线程问题,并且 Xcode 9 与 Google 的 API 结合可能确实会导致此错误。此错误位于调试器中,用于多次崩溃,并且当您切换回 xcode 8 时不再发生崩溃。 你的意思是线程检查器显示的是症状而不是原因。我是说 Xcode 9 beta 既是原因又是症状传播者。 “当您切换回 xcode 8 时,不再发生崩溃”。 XCode 警告和带有调试器读数的实际崩溃之间存在差异。此错误可能显示为警告或崩溃。当切换回 xcode 8 时,崩溃完全消失了。Xcode 9 beta 被称为 beta 是有充分理由的。 是的...我认为对于许多 ios 开发人员来说,仅仅因为某些东西正在显示症状,并不意味着它也可以保证不是原因。我认为也很难接受苹果可能曾经犯过错误。【参考方案3】:

将修改 UI 的代码行封装在 DispatchQueue.main.async 中,以确保它们在主线程上执行。否则,您可能会从不允许修改 UI 的后台线程调用它们。所有这些代码行都必须从主线程执行。

【讨论】:

是的,我已经使用过,但警告仍然存在 @Pang,它是用来打印东西的,不是主要的UI代码。 @MattBlack 尝试在DispatchQueue.main.async 上包装所有修改 UI 的东西 @user6603599-像魅力一样工作【参考方案4】:

参考这个链接 https://developer.apple.com/documentation/code_diagnostics/main_thread_checker

对我来说,当我从块中调用时,这很有效。

【讨论】:

如果您使用的是 Swift 4+,这是最完美的解决方案,感谢您帮助我节省了时间。我玩过 Scheme Editor,但现在遵循 Apple 的建议是最好的选择。 不幸的是,现在这是一个死链接。【参考方案5】:

我认为解决方案已经给出, 我的问题是键盘在路上。

UIKeyboardTaskQueue may only be called from the main thread

【讨论】:

【参考方案6】:

选择方案 -> 诊断,删除主线程检查器,然后警告将消失。 scheme editor

【讨论】:

我认为这不是一个好主意。这里有一个线程检查器发现的问题。禁用线程检查器将使这个和未来的问题未被发现,并导致未来产生技术债务来解决这些问题。 其实我的意思是我们在使用OpenGL es渲染时可以做到这一点,因为我们不能在主线程中渲染帧缓冲区。对吗? 啊,如果没有出现警告,问题就不存在了? "为什么我的烟雾探测器一直响?" “只需卸下电池,问题就解决了。” 如果没有出现警告说明问题不存在?

以上是关于主线程检查器:在后台线程上调用的 UI API:-[UIApplication applicationState]的主要内容,如果未能解决你的问题,请参考以下文章

主线程检查器:在后台线程上调用 UI API iOS 11 Xcode 9 GM Seed

CoreMotion 显然是从后台线程调用 UI API

在 .Net Core 的另一个线程上调用函数

最佳后台处理/API 调用方法?

如何在主线程中执行 do 运算符的 onSubscribe 操作

即使从主线程调用更新,UI 也不会更新