В чем разница между использованием префикса «self.» и записью «self» в списке захвата замыкания?

#swift #closures

#swift #замыкания

Вопрос:

Я писал некоторый код, похожий на:

 class ViewController : UIViewController {
    var foo = "foo"
    override func viewDidLoad() {
        let alert = UIAlertController(title: "", message: "", preferredStyle: .alert)
        alert.addAction(UIAlertAction(title: "", style: .default, handler: { _ in
            print(foo)
        }))
    }
}
  

Xcode сообщает об ошибке, print(foo) которую я забыл зафиксировать self , и предлагает два исправления.:

  1. print(self.foo) , или;

  2. Добавьте список захвата [self] к замыканию: ... handler: { [self] _ in ...

Теперь я помню, что до Xcode 12 / Swift 5.3 он не использовался для этого. Это либо не дает исправлений, либо только первое.

Мой вопрос в том, в чем разница между этими двумя исправлениями? Они каким-то образом захватывают self по-другому?

Руководство по языку, похоже, немного затрагивает это, но, похоже, не говорит, в чем разница между ними.

Комментарии:

1. Просто прочитав из руководства по языку: «Вот версия doSomething(), которая захватывает self, включая его в список захвата замыкания, а затем неявно ссылается на self» (в отличие от 1 или 2 предложений перед ним) — подразумевает, что захват [self] позволяет записать экранирующее замыкание, ссылающееся наself неявно повсюду (против необходимости писать его явно). Итак, похоже, это вопрос предпочтений

2. Просто для справки, это новая функция, реализованная в Swift v5.3: SE-0269 — Повышение доступности implicit self в @escaping замыканиях, когда ссылочные циклы маловероятны

Ответ №1:

Эти два функционально одинаковы, оба явно захватывают self . [self] Шаблон позволяет избежать необходимости засорять замыкание повторяющимися self. ссылками, если у вас было несколько ссылок на свойства и / или методы. Но они оба делают в точности одно и то же — захватывают self .

Как говорится в документе, на который вы ссылаетесь:

Обычно замыкание захватывает переменные неявно, используя их в теле замыкания, но в этом случае вам нужно быть явным. Если вы хотите захватить self , напишите self явно, когда вы его используете, или включите self в список захвата закрытия.

Суть в том, что это одно и то же.


Как бы то ни было, другой вариант — self вообще избегать захвата. Например, вы можете только захватывать foo :

 alert.addAction(UIAlertAction(title: "", style: .default) { [foo] _ in
    print(foo)
})