Как указывали другие, ссылочные типы всегда передают указатель на объект, который идеально подходит для того, где вы хотите «общее, изменяемое состояние» (как указано в упомянутом документе). Ясно, однако, если вы мутируете/получаете доступ к ссылочному типу по нескольким потокам, убедитесь, что вы синхронизировали свой доступ к нему (через выделенную последовательную очередь, шаблон считывателя, блокировки и т. Д.).
Соотношения типов немного сложнее. Да, как указывали другие, если вы передаете тип значения в качестве параметра методу, который затем выполняет что-то в другом потоке, вы, по сути, работаете с копией этого типа значений (примечание Джоша о копировании в режиме on- пишите, несмотря на это). Это обеспечивает целостность объекта, переданного методу. Это нормально (и здесь были достаточно охвачены другие ответы).
Но это осложняется, когда вы имеете дело с закрытием. Рассмотрим, например, следующее:
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
вы ссылаетесь в закрытии такой же экземпляр, с которым вы имеете дело в основном потоке (или в любом другом потоке, на котором это выполнялось), а не на его копии. Если вы имеете дело с изменяемым объектом, это не является потокобезопасным.
Я придумал этот пример, чтобы проиллюстрировать этот момент, где в заявлении print
в закрытии выше будет сообщаться «Рейчел Райан», фактически показывающий состояние типа значения Person
в несогласованном состоянии.
С затворами с использованием типов значений, если вы хотите наслаждаться семантикой значений, вы должны изменить этот 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
заявление будет правильно сообщить «Роб Райан» даже Тхо ugh оригинал person
объект мутируется в другом потоке.
Итак, если вы имеете дело со типами значений и замыканиями, типы значений могут делиться по потокам, если вы явно не используете список захвата (или что-то подобное), чтобы использовать семантику значений (т.копируя объект по мере необходимости).
Когда я впервые прочитал ответы здесь, я подумал, что тип значения NO NO имеет многопоточную проблему. Но это верно, если вы захватили/скопировали. Если вы манипулируете * точным * ** одинаковым значением **, то у вас есть ** проблемы ... – Honey
* Типы значений немного сложнее. Да, как указывали другие, если вы передаете тип значения в качестве параметра методу, который затем делает что-то в другом потоке * <- Не пишете ли вы немного запутанно? Строка не может быть мутирована при передаче другой функции того же потока. Скажем, я передал структуру в func, параметр внутри func неизменен ... – Honey
Да, если вы передадите тип значения методу, этот метод получит его копию, поэтому, если вы его мутируете, вы мутируете копия и оригинал безопасно нетронутыми. Но, как я говорю в следующем параграфе, «отдельный поток получает копию типа значения», обсуждение более сложное, поскольку это поведение копирования не происходит при использовании закрытий. И исходный вопрос заключался не в передаче типов значений в качестве параметров, а в более общем отношении о том, будут ли другие потоки всегда получать копии типов значений. Нижняя линия, типы значений, используемые в закрытии, не копируются (без списков захвата, по крайней мере). – Rob