如何使用Foundation框架在Swift中创建和运行线程?

3
我想在Swift中创建一个简单的线程。
我之前没有Objective-C的经验,因此我认为文档很难理解,包括像“receiver”、“selector”和“target”这样的术语 - 有人可以澄清一下吗?
我当前尝试从函数启动线程而不是子类化Thread的方法如下: https://developer.apple.com/documentation/foundation/thread
import Foundation

func run(msg: String) -> Void {
    for i in 1...1000 {
        print("\(i):\(msg)")
    }
}

let t = Thread(target: nil, selector: run, object: "helloworld")
t.start()

然而,这个例子无法运行,我在网上也找不到任何好的示例。

有人可以提供一个可工作的例子并进行解释吗?


3
你应该研究一下 Grand Central Dispatch。 - Joakim Danielson
2
一些资源:线程编程指南并发编程指南。后者鼓励您使用“Grand Central Dispatch”(GCD)而不是线程。 - Martin R
1
Grand Central Dispatch是一种线程处理的方式。这种方式是合法的。GCD有其自身的特点和限制。 - WestCoastProjects
2个回答

4

Thread(在Objective-C中为NSThread)是对POSIX线程的封装。它有一些额外的管道,用于与苹果的Foundation框架交互(这在iOS和Mac OS之间很常见)。

目标和选择器的概念是Objective-C运行时的一部分,它是Mac OS上AppKit和iOS上UIKit的基础。选择器是Objective-C动态方法分配系统的方法签名。目标是使用特定选择器接收方法调用的对象。

请注意,Thread类还具有使用闭包而不是目标/操作的初始化程序。如果您决定使用Thread,则可以改用该初始化程序(它将在启动时调用您的闭包)。该初始化程序为convenience init(block: @escaping () -> Void)

正如其他人所说,出于以下几个原因,最好使用Grand Central Dispatch(GCD):

  • 它不绑定到Objective-C运行时。

  • 创建和销毁线程非常昂贵,需要内核调用,并在线程的生命周期中占用物理内存。

GCD会代表您在系统上保留线程池。您可以使用现有的调度队列或创建自己的调度队列,系统将从线程池中决定将哪些线程分配给该队列。

相对于Thread(即NSThread)类,使用Swift更加简洁明了。

请注意,您还可以忽略Thread类并直接创建POSIX线程,但我不建议这样做。


谢谢!我怎么知道@escaping是指闭包?我不打算编写iOS或类似的应用程序,而是将一些Python应用程序移植到Swift以学习这种语言。在这种情况下,您仍然建议使用GCD吗,还是它只能用于应用程序开发? - Shuzheng
它并不是@escaping部分表明这是一个闭包(虽然它是一个闭包的限定词)。而是() -> Void那一部分。这是一个不接受参数且不返回结果的闭包签名。 - Duncan C
GCD通常比线程更好地实现并发。因此,我建议使用GCD。听起来你将会开发Mac应用程序。请注意,Python和Swift非常不同。Python是一种弱类型解释性语言 - 一种脚本语言。Swift是一种适用于系统编程的编译语言。 - Duncan C
有没有一些示例的“Thread”代码可以提供,而不是不断地说“GCD更好”?我早在九十年代就已经使用了强大的本地线程和Java中的“java.util.concurrent” - 现在想要使用Objective C和/或Swift提供的更接近本地的模型,而不是“dispatch”模型。 - WestCoastProjects
我曾经使用过 NSThread(现在的 Thread)编写相当复杂的应用程序,它是在 POSIX 线程之上的一个很薄的层。那段代码是用 Objective-C 写的,而且有些老旧了,所以我不确定它是否还具有参考价值。(自那时起我已经转向 GCD,所以我没有更新的基于线程的代码)如果您要像在 Thread 类中使用低级别的线程,则需要非常注意创建和销毁线程的成本,以及线程占用物理内存的情况。GCD 使用共享线程池,因此可以避免创建和销毁线程的开销。 - Duncan C

1
你可以使用这段代码作为示例,但正如评论中提到的那样,你可以使用 Grand Central Dispatch 或 Operations 代替。它们是更现代的多线程方式。
class A {
    func start() {
        let t = Thread(target: self, selector: #selector(run(msg:)), object: "helloworld")
        t.start()
    }

    @objc func run(msg: String) {
        for i in 1...1000 {
            print("\(i):\(msg)")
        }
    }
}

let a = A()
a.start()

谢谢,你能解释一下选择器是什么,为什么要使用#selector,以及@obj的目的是什么吗? - Shuzheng
@Shuzheng 很好的答案 https://dev59.com/lGAg5IYBdhLWcg3wE3jQ 这里您可以学到 Selector 是什么 https://developer.apple.com/library/archive/documentation/General/Conceptual/DevPedia-CocoaCore/Selector.html - Taras Chernyshenko
谢谢。另外,你的示例无法运行?它说需要Objective-C运行时? - Shuzheng
“selector” 只能与 Objective-C 运行时一起使用,因为它基于消息分发。 - Taras Chernyshenko
1
这是一种不同的多线程处理方式。普通的线程实际上更强大,也不那么古怪。 - WestCoastProjects

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