Доступ к self из свойств экземпляра, которые являются замыканиями

#swift

#swift

Вопрос:

Я использую Xcode6-beta2, но у меня была такая же проблема с самого первого публичного бета-тестирования. Мой подкласс Swift Obj-C UIViewController выглядит следующим образом:

 class SomeVC: UIViewController {
    var c1: () -> () = {
        println(self)
    }

    var c2: () -> () {
        get {
            return { println(self) }
        }
    }

    var c3: () -> () {
        return { println(self) }
    }

    override func viewDidAppear(animated: Bool) {
        super.viewDidAppear(animated)
        c1()
        c2()
        c3()
    }
}
 

Когда отображается VC, я вижу распечатанные следующие строки:

 (Function)
<_TtC12SwiftiOSTest6SomeVC: 0x10bf1ed10>
<_TtC12SwiftiOSTest6SomeVC: 0x10bf1ed10>
 

(c2 и c3 отличаются только тем, что нет необходимости включать get {…} для вычисляемого свойства, если оно доступно только для получения.)

Итак, self первого замыкания, похоже, относится к самому типу функции / замыкания, тогда как self других относится к контроллеру представления (как я и ожидал). Единственное различие между c1 и c2 / c3 заключается в том, что первое является сохраненным свойством, последние являются вычисляемыми свойствами, но я все равно ожидаю, что замыкания и их захваченные значения будут одинаковыми, т. Е. self всегда будет ссылаться на окружающий класс. Похоже, что сейчас нет очевидного способа для закрытия c1 получить доступ к методам / свойствам окружающего класса.

Это что-то задокументированное где-то (я прочитал книгу Swift и ничего не нашел), или это просто какая-то ошибка бета-компилятора, которая должна быть где-то зарегистрирована?

Ответ №1:

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

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

Вам не хватает @lazy, так что self это неизвестно закрытию, поэтому оно печатает его, как (Function) я предполагаю.

 class TableViewController: UIViewController {
var name = "anil"
// Since swift 2.0 came out @lazy is replaced by lazy
lazy  var c1: () -> () = {
    println(self)
    println(self.name)

}

var c2: () -> () {
get {
    return { println(self) }
}
}

var c3: () -> () {
return { println(self) }
}


  override func viewDidLoad() {
        super.viewDidLoad()
        c1()
        c2()
        c3()
        }
}
 

Вывод

<_TtC12TableViewApp19TableViewController: 0x10d54e000>
anil
<_TtC12TableViewApp19TableViewController: 0x10d54e000> <_TtC12TableViewApp19TableViewController: 0x10d54e000>


Обновить

Назначение замыкания переменной экземпляра класса приводит к сильному циклу ссылок. Вам следует избегать этого. Для этого Swift использует список захвата

Если вы назначаете замыкание свойству экземпляра класса, и замыкание захватывает этот экземпляр, ссылаясь на экземпляр или его члены, вы создадите сильный ссылочный цикл между замыканием и экземпляром. Swift использует списки захвата для разрыва этих сильных ссылочных циклов. Для получения дополнительной информации см. Сильные ссылочные циклы для замыканий.

Таким образом, правильное использование замыкания может быть

 @lazy  var c1: () -> () = {
    [unowned self] in
    println(self)
    println(self.name)

}
 

Ссылка: руководство по программированию Swift

@lazy
Edit был изменен на lazy

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

1. Я попробовал это, и я получил эту ошибку компилятора: ‘SomeVC -> () -> SomeVC!’ не имеет члена с именем ‘name’. Но ваш код отличается от моего еще в одном аспекте — @lazy keywoard / директива / что бы это ни было. Когда я добавляю это перед определением замыкания c1 , оно работает, мне даже не нужно имя var. Это еще более запутанно, какое отношение к чему-либо здесь имеет ленивый инициализация?

2. Так что это похоже на ошибку компилятора, не так ли?

3. Нет необходимости в переменной экземпляра, это было мое недоразумение. Вы можете опустить переменную. но требуется @lazy .

4. Хорошо, я нашел этот отрывок в книге Swift от Apple, так что это задокументировано без проблем с компилятором. Тем не менее, очень, очень запутанно. Спасибо за помощь в прояснении этого.

5. Да, я знаю об этом, и мой код на самом деле не использует сильные ссылки на self (я использую [слабый self], потому что unowned взорвался на мне, не знаю почему. Мне нужно еще раз прочитать главу ARC. Я не использовал его в своем первоначальном сообщении, чтобы упростить минимизацию беспорядка.