Свифт, почему методам классов не нужны закрывающие списки

Если функции, по сути, замыкания. Почему методам класса не нужны списки замыканий при обращении к self или другому свойству экземпляра внутри замыкания.

Есть ли за кадром [неизвестное Я]? Например:

class MyClass{
    func myFunc(){
        self.otherFunc()
    }

    func otherFunc(){
        print()
    }
}

Разве в myFunc не будет ссылочного цикла? Т.е. замыкание указывает на себя, а экземпляр указывает на функцию. Ни один не мог быть освобожден.

2 ответа

Решение

"Если функции по сути являются замыканиями". Это не правда Функции (и методы) - это не то же самое, что замыкания. Все функции имеют свободные переменные без привязки. Замыкания связывают некоторые или все свои свободные переменные (закрытые над ними, отсюда и название "замыкание").

"Свободная переменная" - это любая переменная, определенная вне области действия функции (включая ее формальные параметры). Функция верхнего уровня func f(x: Int) имеет одну свободную переменную; когда вы вызываете его, вы должны передать параметр. Закрытие как { f(1) } не имеет свободных переменных. Когда вы звоните, вы не передаете никаких параметров.

Метод, как и функция, ничего не захватывает. Он передается всем своим свободным переменным, когда он выполняется. Например, когда вы делаете звонок object.doThis()это тоже самое что звонить Type.doThis(object)(),

class X {
    func doThis() {}
}

let x = X()
x.doThis()

X.doThis(x)() // Same thing

X.doThis(x) это функция, которая возвращает функцию. Здесь нет магии. Все свободные переменные предоставляются во время звонка. Ничего не захвачено. ("Свободная переменная" в описываемом вами случае self, но это ничего не меняет. self не является особенным, за исключением того, что он получает немного синтаксического сахара вокруг него.)

Это отличается от закрытия:

let c = { x.doThis() }
c()

Когда я звоню c()откуда он знает значение x? Возможно я вернулся c а также x может быть вне области сейчас. Система должна отслеживать x (включая создание сильной ссылки, чтобы она не освобождалась), и она делает это, захватывая ее или "замыкаясь по x", что повышает вероятность сохранения циклов. Так в c, x связан. Это не бесплатно. Вы не можете передать это, когда вы звоните c(),

self здесь не особенный Это просто другая переменная. [weak self] в замыканиях тоже ничего особенного. Ты можешь написать [weak x] точно также. [...] синтаксис просто список захвата.

Закрытия могут вызывать контрольные циклы только тогда, когда замыкание остается в живых. Учти это:

let foo = MyClass()
let bar: () -> () = { in
    print(foo)
}

bar закрытие содержит ссылку на foo, но эта ссылка исчезает, когда ничего не указывает bar больше. Например:

func f(foo: MyClass) {
    let bar: () -> () = { () in
        print(foo)
    }
}

Это не создает ссылочный цикл, потому что когда f возвращается, закрытие в bar уничтожен Точно так же, когда вы звоните myFunc а также otherFuncвам нужна сильная ссылка на self (компилятор гарантирует, что он у вас есть), но так как он вам больше не нужен в конце функции, цикл не создается.

В общем случае замыкание не будет систематически создавать ссылочный цикл, даже если оно @escaping, Рассмотрим случай Dispatch.async:

class MyClass {
    func foo() {
        DispatchQueue.main.async {
            print(self)
        }
    }
}

Это на самом деле не создает ссылочный цикл, потому что даже если ссылки закрытия self какое-то время, self не ссылается на закрытие.

Это опасный случай:

class MyClass {
    var closure: () -> ()

    func f() {
        self.closure = {
            print(self)
        }
    }
}

Этот фактически создает ссылочный цикл: self.closure имеет сильную ссылку на self, а также self имеет сильную ссылку на self.closure,

Другие вопросы по тегам