1

如果函数本质上是闭包。为什么在闭包中引用 self 或另一个实例属性时,类的方法不需要闭包列表。

幕后有[无主的自我]吗?例如:

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

    func otherFunc(){
        print()
    }
}

myFunc 中不会有引用循环吗?即闭包指向自身,实例指向函数。两者都不能被释放。

4

2 回答 2

3

只有当闭包保持活动状态时,闭包才会导致引用循环。考虑一下:

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

bar闭包包含对 的引用,foo但是一旦bar不再引用,该引用就会消失。例如:

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

这不会创建引用循环,因为当f返回时,其中的闭包bar 被销毁。同样,当您调用myFuncand时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

于 2018-05-08T22:41:39.600 回答
3

“如果函数本质上是闭包。” 这不是真的。函数(和方法)与闭包不同。函数的所有自由变量都未绑定。闭包绑定了它们的部分或全部自由变量(封闭在它们之上,这就是名称“闭包”的来源)。

“自由变量”是在函数范围之外定义的任何变量(包括其形式参数)。顶层函数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?我可能已经返回cx现在可能超出范围。系统必须跟踪x(包括进行强引用以便它不会释放),并且它通过捕获它来做到这一点,或者“关闭 x”,这会增加保留循环的可能性。所以在c,x是有界的。它不是免费的。你打电话时不能通过它c()

self这里并不特别。这只是另一个变量。[weak self]in 闭包也不是特别的。你也可以写[weak x][...]语法只是捕获列表。

于 2018-05-08T23:49:10.040 回答