Swift:如何将工作人员放入单独的线程中,在主视图中显示结果?

问题描述

我想知道是否有一种简单的方法可以做到这一点:将长时间运行的工作线程放入单独的线程中,以免阻塞 UI。并在主视图中显示结果。在 SwiftUI 或 UIKit 下。我在网上找到的都是非常复杂的。还是 Swift 中有完全不同的方法

我制作了一个简约的 Python 程序来展示我想要做什么。在 MacOS 中显示 WiFi 信号强度。

import time
import sys
import subprocess
from PyQt5.QtWidgets import QApplication,QWidget,QLabel,QHBoxLayout
from PyQt5.QtCore import QObject,pyqtSignal,QThread

class Worker(QObject):
    send_output = pyqtSignal(str)
    def __init__(self):
        super().__init__()

    def worker(self):
        while True:
            _out = subprocess.check_output(
                ["/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport","-I"])\
                .decode("utf-8")
            self.send_output.emit("RSSI: " + _out.splitlines()[0][-3:] + " dBm")
            time.sleep(2)

class MainWindow(QWidget):
    do_work = pyqtSignal(object)
    def __init__(self):
        super().__init__()

        self.label = QLabel()
        layout = QHBoxLayout()
        self.setLayout(layout)
        layout.addWidget(self.label)
        self.show()

        self.app_thread = QThread()
        self.app = Worker()
        self.app.movetoThread(self.app_thread)
        self.app.send_output.connect(self.output_label)
        self.app_thread.started.connect(self.app.worker)
        self.app_thread.start()

    def output_label(self,_str):
        self.label.setText(_str)

if __name__ == '__main__':
    application = QApplication(sys.argv)
    mainwindow = MainWindow()
    sys.exit(application.exec())

我现在正在努力寻找进入 Swift 的方法。这很令人兴奋,但确实是一件大事。提前致谢!

解决方法

这是一个非常宽泛的问题,所以我也将回答这个问题。

是的,您可以在单独的线程中运行任务,然后将数据返回到主线程。有很多方法可以做到这一点,一种常见的方法是 DispatchQueue

让我们举一个过于简化的例子(绝对不是真实世界的代码):

struct ContentView : View {
    @State var result = ""
    
    var body: some View {
        Text("Result: \(result)")
            .onAppear {
                DispatchQueue.global(qos: .background).async {
                    //do your long-running code
                    var innerResult = 0
                    for i in 0...1000000 {
                        innerResult += 5
                        print(i)
                        //use DispatchQueue.main.async here if you want to update during the task
                    }
                    
                    //update at the end:
                    DispatchQueue.main.async {
                        result = "Done! \(innerResult)"
                    }
                }
            }
    }
}

在这个例子中,当视图出现时,一个任务在后台线程中运行,通过 DispatchQueue。在这种情况下,我只是利用这样一个事实,即打印到控制台是一项相当昂贵的操作,如果执行一百万次。

当它完成时,它会分派回主线程并更新状态变量中的结果。

DispatchQueue 并非特定于 UIKit 或 SwiftUI —— 使用 SwiftUI 将演示放在一起是最简单的。

如果您要真正开始编写代码来执行此操作,您需要对 DispatchQueues 的工作方式进行一些研究,包括使用哪个队列、是否创建自己的队列、是否希望按顺序完成任务等,但对于这是否可以完成(以及如何容易)的广泛问题,这至少显示了基础知识。