Почему замыкание захватывает ссылку, а функция - нет?Кроме того, почему ключевое слово "lazy" требуется для объявления закрытия? - PullRequest
0 голосов
/ 26 февраля 2019

Я экспериментировал со следующим кодом в Xcode Playground:

class X {

    var a = 3

    init(a: Int) {
        self.a = a
    }

    deinit {
        print("\(self.a) is deallocated.")
    }

    func returnX() -> Int {
        return self.a
    }

    lazy var anotherReturnX: () -> Int = {
        return self.a
    }
}

var e: X? = X(a: 6)
print(e!.returnX())
e = nil // prints "6 is deallocated."

var f: X? = X(a: 7)
print(f!.anotherReturnX())
f = nil // prints nothing

Из приведенного выше кода я вижу, что в функции returnX() нет ссылки, поэтому e освобождается один разЯ установил e на nil.Однако ссылка сохраняется в закрытии anotherReturnX(), поэтому f не освобождается.По-видимому, это подразумевает, что замыкание захватывает ссылки, а функция - нет.

Кроме того, когда я впервые набирал код, я не включал ключевое слово lazy перед объявлением замыкания, как я думал, что это будетбыть ненужным, чтобы сделать это.Однако это вызывает ошибку во время компиляции.Я предполагаю, что, поскольку к замыканию можно получить доступ только после создания экземпляра, он должен получить доступ к созданному экземпляру self.Но поскольку то, что я здесь заявляю, фактически является «анонимной функцией», зачем вообще закрытому доступу к self во время создания экземпляра?

После некоторых размышлений я обнаружил больше противоречий.Например, я понимаю, что ссылка захватывается при вызове замыкания.Однако во время инициализации X я просто назначаю закрытие переменной , не вызывая ее , так же, как объявление других свойств экземпляра.Таким образом, закрытие не должно ничего делать во время инициализации, и компиляция кода без ключевого слова lazy должна быть в порядке.Но компиляция не удалась.Я не уверен, что идет не так в моем понимании.

Я прочитал некоторые связанные статьи, такие как сильная / слабая ссылка, сохранить цикл, ленивое хранимое свойство.Однако многие объясняют «что происходит» и мало говорят о «почему».

Итак, помимо вопроса, поднятого в заголовке, я также хотел бы уточнить, что отличает функцию и замыкание от других, так что возникает описанная выше ситуация?

Обновление:

Тот факт, что замыкание захватывает ссылку, в то время как функция не применяется, еще больше "навязывается" мне, поскольку, согласно компилятору Xcode, я могу переписать return self.a как return a в returnX(), но я не могу сделать это вanotherReturnX.Таким образом, я полагаю, я должен был бы согласиться с тем, что, хотя функция и замыкание схожи, поскольку каждая из них является «набором функциональных возможностей», функция отличается от замыкания тем, что она не захватывает ссылки.Если бы я углубился в причину этого, это, вероятно, потребовало бы разработки самого Swift?

Однако я все еще не могу понять, почему ключевое слово lazy требуется для объявления закрытия.

Ответы [ 2 ]

0 голосов
/ 12 марта 2019

returnX - метод класса.Методы не захватывают переменные.self - это неявная локальная переменная в методах, которая неявно передается методу при вызове метода.

anotherReturnX - это свойство, которому лениво назначается замыкание.Это закрытие захватывает внешние переменные, которые используются внутри него, включая self.Этот захват создает надежную ссылку из замыкания на экземпляр X, что в сочетании с сильной ссылкой из экземпляра X на замыкание создает цикл сохранения.

0 голосов
/ 26 февраля 2019
lazy var anotherReturnX: () -> Int = {
    return self.a
}

Я здесь - это сильное Я.Когда объект сильно ссылается на другой объект, ARC не может освободить его, поэтому создается цикл сохранения.Ссылка должна быть слабой, чтобы избежать цикла сохранения путем создания слабой собственной личности внутри блока.

lazy var anotherReturnX: () -> Int = { [weak self] in
    return self?.a
}
...