Xcode 9 更新 Swift 后出现紫色警告

5

更新到Xcode 9后,我的项目(最初是在Swift 3中创建的)出现了紫色警告:UITextField.text仅可从主线程中使用

我只在if语句中检查文本字段是否为空…附属截图。

enter image description here


2
请尝试我的答案提供的解决方案。这里有一个类似的参考链接,我曾经遇到并解决过:https://dev59.com/SlcP5IYBdhLWcg3wcZm0 - Krunal
6个回答

14

登录管理器正在非主线程上运行您的闭包,因此您不能在主线程之外使用用户界面元素。

其他人已经说过,在辅助线程上仅读取UI属性是可以的,他们可能是正确的,但是没有绝对的保证,个人而言,我会遵循警告。例如,访问这些属性可能会有副作用。也许它们的值被缓存在某个地方,而主线程正在更新或删除该属性,恰好在你想访问它的时候。

消除警告的正确方法是在主线程上执行您的闭包,例如:

LoginManager.checkLoginStatus {
    isLoggedIn in

    DispatchQueue.main.async {

        // Do all your UI stuff here

    }
 }

这种方式下,运行闭包的线程除了在主队列上安排代码运行之外什么都不做,而主队列始终在主线程上运行。


7

只需添加DispatchQueue.main.async即可防止此问题,而且这也是Swift文档推荐的方法。

DispatchQueue.main.async { 
    //code that caused error goes here
}

6
XCode 9包含一个主线程检查器工具,用于检查是否在主线程上发生任何与UIKit相关的事情。它旨在防止从后台线程触摸UIKit会创建问题的情况。
虽然建议保留此工具,但如果您认为它太麻烦,可以将其关闭:
XCode->编辑方案->目标->诊断->取消选中运行时API检查。

enter image description here


2
这里是苹果关于“主线程检查器”的指南:
  • Xcode 9 中新增的主线程检查器。
    • 可检测来自后台线程的 UI API 不当使用。
    • 可检测未在主线程上进行的 AppKit、UIKit 和 WebKit 方法调用。
    • 在调试期间自动启用,可在方案编辑器的诊断选项卡中禁用。
    • 主线程检查器可用于 Swift 和 C 语言。

尝试使用以下代码替换您的代码,并查看效果。这是一种永久性的解决方案,无需禁用警告。

@IBAction fund saveButtonTapped(_ sender: Any) {


    LoginManager.checkLoginStatus { isLogged in

        if isLogged {

            // Updates your UIs on main queue
            DispatchQueue.main.async(execute: {

            if self.priceTextField.text == "" {
                GlobalVariables.sharedManager.itemPrice = 0
            } else {
                GlobalVariables.sharedManager.itemPrice = Double(self.priceTextField.text!)!
            }

            if self.titleTextView.text == "" {
                GlobalVariables.sharedManager.expenseTitle = "N/A"
            } else {
                GlobalVariables.sharedManager.expenseTitle = self.titleTextView.text!
            }

            if self.descriptionTextView.text == "" {
                GlobalVariables.sharedManager.expenseDescription = "N/A"
            } else {
                GlobalVariables.sharedManager.expenseDescription = self.descriptionTextView.text!
            }

            if self.categoryPickerTextFld.text == "" {
                GlobalVariables.sharedManager.expenseCategory = "N/A"
            } else {
                GlobalVariables.sharedManager.expenseCategory = self.categoryPickerTextFld.text!
            }


            if self.imageView.image == nil {
                GlobalVariables.sharedManager.expenseSelectedImage = nil
            } else {
                GlobalVariables.sharedManager.expenseSelectedImage = self.imageView.image
            }


            }) 

        }
    }
}

或者,您可以通过禁用项目目标中的主线程检查器来禁用警告。
(Xcode项目目标 - 设备列表下方)>>编辑方案>>目标>>诊断>>运行时API检查-取消选中。

enter image description here


1
with the help of OCR tool - Krunal

2

问题的确在于您正在从后台线程使用UI元素。请尝试修改代码以便从主线程访问它们。

Xcode 9具有新功能-它可以在运行时检查此类问题并在执行期间报告它。

如果您真的不想要它,可以在方案设置中禁用它。编辑方案...->(选择您的方案)->诊断->禁用“主线程检查器”


感谢您的回复。我认为解决问题而不仅仅是禁用警告是有意义的。您能推荐一个解决方案吗?我不确定如何将IF语句导入到主队列中... - KirillC
DispatchQueue.main.async {if self.priceTextField.text == "" { GlobalVariables.sharedManager.itemPrice = 0 }else{ GlobalVariables.sharedManager.itemPrice = Double(self.priceTextField.text!)! } } 我认为这可能行不通... - KirillC
你已经尝试过使用 DispatchQueue.main.async 了吗?我认为这会起作用并且很有意义 - 在后台线程上执行一些耗时的操作(例如登录),然后回到主线程以更新UI。 - Anton Malmygin

0

警告似乎过于激进,因为您仅检查textField是否为空,而不会更改UI上的任何内容。该警告试图阻止在主线程之外使用UI元素,这当然不是坏事,但它没有考虑到像您这样的边缘情况。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接