我正在阅读Apple的文档。我以为我知道何时选择值类型和何时选择引用类型,但是我回到了Swif101。该文档说:
值类型: 数据将在多个线程的代码中使用。 参考类型: 您要创建共享的可变状态
引用类型也跨多个线程共享吗?这两行有什么区别?
正如其他人指出的那样,引用类型始终将指向对象的指针传递给对象,这是您想要“共享的可变状态”的理想选择(正如您所引用的文档所述)。但是,很显然,如果要在多个线程之间进行突变/访问引用类型,请确保同步对它的访问(通过专用的串行队列,读写器模式,锁等)。
但是,值类型稍微复杂一些。是的,正如其他人指出的那样,如果将值类型作为参数传递给在另一个线程上执行某操作的方法,则实际上是在使用该值类型的副本(乔什关于尽管写)。这样可以确保传递给该方法的对象的完整性。很好(并且这里的其他答案已充分覆盖了该问题)。
但是当您处理闭包时,它变得更加复杂。例如,考虑以下内容:
struct Person { var firstName: String var lastName: String } var person = Person(firstName: "Rob", lastName: "Ryan") DispatchQueue.global().async { Thread.sleep(forTimeInterval: 1) print("1: \(person)") } person.firstName = "Rachel" Thread.sleep(forTimeInterval: 2) person.lastName = "Moore" print("2: \(person)")
显然,您通常不sleep会这样做,但我这样做是为了说明这一点:即,即使我们正在处理值类型和多个线程,person闭包中的引用也与您处理的实例 相同 在主线程(或运行该线程的任何线程)上,而不是其副本。如果要处理可变对象,则不是线程安全的。
sleep
person
我设计了这个示例来说明这一点,在print上面的闭包中的语句将报告“ Rachel Ryan”,有效地显示了处于Person不一致状态的值类型的状态。
print
Person
对于使用值类型的闭包,如果您想享受值语义,则必须更改该async调用以使用单独的变量:
async
let separatePerson = person queue.async { Thread.sleep(forTimeInterval: 1) print("1: \(separatePerson)") }
或者,甚至更简单,使用“捕获列表”,它指示闭包应捕获哪些值类型变量:
queue.async { [person] in Thread.sleep(forTimeInterval: 1) print("1: \(person)") }
使用以上两个示例中的任何一个,您现在都在享受值语义,复制对象,并且print即使原始person对象在另一个线程上发生了变异,该语句也将正确报告“ Rob Ryan” 。
因此,如果要处理值类型和闭包,则可以在线程之间共享值类型,除非您显式使用捕获列表(或等效的东西)以享受值语义(即根据需要复制对象)。