Есть несколько незначительных изысков в вашем вопросе, из-за которых сложно дать четкий ответ, но я думаю, что понимаю основную проблему, и короткий ответ - «нет».Но ваш пример невозможен, поэтому ответ «это невозможно».И если бы это было возможно, не было бы сильной ссылки (да и не было бы необходимости в ней), поэтому вопрос все равно был бы вроде «это невозможно».Тем не менее, давайте пройдемся по тому, что здесь происходит.
Во-первых, closure
не может ссылаться на localString
, если оно не переназначено каким-либо образом в комментарии внутри doStuff()
.closure
назначается на уровне, где localString
находится вне области видимости.Замыкания могут захватывать только те переменные, которые находятся в области видимости, когда они назначены, а не когда они вызываются.Но вернемся к исходной версии этого вопроса, прежде чем он был отредактирован.В этой версии был описанный вами случай:
@objc final myClass : NSObject
{
let classPropertyString = "A class property"
func doStuff()
{
let localString = "An object local to this function"
DispatchQueue.global(qos: .userInitiated).async { [classPropertyString] in // (1)
// Do things with 'classPropertyString' and 'localString'
}
// (2)
}
}
Здесь нет проблем.classPropertyString
копируется в замыкание, избегая любых удерживающих петель.На localString
ссылается закрытие, и поэтому оно сохраняется до тех пор, пока существует закрытие.
Поскольку вы перечислили classPropertyString
в списке захвата, оно оценивается в точке (1) и копируется в закрытие,Поскольку вы неявно захватили localString
, он рассматривается как ссылка.См. Списки захвата в Справочнике по языку программирования Swift, где приведены отличные примеры того, как это работает в разных случаях.
Ни в коем случае (*) Swift не разрешит базовое хранилище для чего-то, что вы 'использовать в закрытом состоянии, чтобы скрыться за вашей спиной.Вот почему типичная проблема - чрезмерные удержания (утечки памяти), а не свисающие ссылки (сбои).
(*) «Ни в коем случае» здесь ложь.Есть несколько способов, которыми Swift позволит это, но почти все они включают «Небезопасный», который является вашим предупреждением об этом.Основным исключением является unowned
и, конечно, все, что связано с !
типами.А Swift, как правило, не ориентирован на многопоточность, поэтому вы должны быть осторожны с этим ...
Последний комментарий о безопасности потоков - это место, где тонкие различия между неявным и явным захватом действительно могут иметь значение.Рассмотрим этот случай, когда вы изменяете неявно захваченное значение в двух очередях:
func doStuff() -> String
{
var localString = "An object local to this function"
DispatchQueue.global(qos: .userInitiated).async {
localString = "something else"
callFunction(localString)
}
localString = "even more changes"
return localString
}
Что происходит в этом случае?Боже мой, никогда не делай этого.Я верю это неопределенное поведение и что localString может быть что угодно , включая поврежденную память, по крайней мере в наиболее общем случае (это может быть определенное поведение для вызова .async
; я неконечно).Но не делайте этого.
Но для ваших обычных случаев нет причин явно захватывать локальные переменные.(Иногда мне хотелось бы, чтобы Свифт прошел путь C ++ и сказал, что было требуется, но это не так.)
Хорошо, еще один неявный и явный отличия, которые могут привести к тому, какони работают.Рассмотрим замыкание с сохранением состояния, подобное этому (я строю это довольно часто):
func incrementor() -> () -> Int {
var n = 0
return {
n += 1
return n
}
}
let inc = incrementor()
inc() // 1
inc() // 2
inc() // 3
let inc2 = incrementor()
inc2() // 1
Посмотрите, как локальная переменная n
захватывается замыканием и может быть изменена после выхода из области видимости.И посмотрите, как у inc2
есть собственная версия этой локальной переменной.Теперь попробуйте это с явным захватом.
func incrementor() -> () -> Int {
var n = 0
return { [n] in // <---- add [n]
n += 1 // Left side of mutating operator isn't mutable: 'n' is an immutable capture
return n
}
}
Явные захваты являются копиями, и они неизменны.Неявные перехваты являются ссылками и поэтому имеют ту же изменчивость, что и вещи, на которые они ссылаются.