3
class Name {
    var name: String
    init(name: String) {
        self.name = name
    }
    deinit {
        print("\(name) deinit")
    }
}
var x: Name? = Name(name: "abc")

var someClosure = {
    print("\(x?.name)")
}

someClosure()

x = nil

然后控制台会输出:

Optional("abc")
abc deinit

可以看出调用了“deinit”函数。所以不会形成强参考循环。但是如果我在闭包中添加一个捕获列表:

var someClosure = { [x] in
    print("\(x?.name)")
}

控制台将输出:

Optional("abc")

并且没有调用“deinit”函数。所以对象和引用形成了一个强引用循环。

是什么原因?这两个条件有什么区别?

4

2 回答 2

1

首先,在这两种情况下都没有强保留循环——你只是有一个全局闭包变量,它持有对你的类实例的强引用,因此防止它被释放。

在您的第一个示例中,当您x在闭包中捕获时:

var someClosure = {
    print("\(x?.name)")
}

你得到的(实际上)是对引用的引用——也就是说,闭包有一个对存储的引用x,然后它对你的类实例有一个引用。当您设置xnil- 时,闭包仍然具有对 的存储的引用x,但现在x 没有对您的类实例的引用。因此,您的类实例不再具有对其的任何强引用,并且可以被释放。

在第二个示例中,当您使用捕获列表时:

var someClosure = { [x] in
    print("\(x?.name)")
}

您正在复制 x自己——也就是说,您正在复制对类实例的引用。因此,只要您的课程存在,关闭就会保留您的课程。设置xnil不会影响闭包对您的实例的引用,因为它有自己的强引用。

于 2017-04-15T07:59:11.863 回答
0

这就是我认为这里发生的事情。

ARC 将引用计数添加到x创建时间x。当使用捕获列表调用闭包时,它会添加另一个计数(闭包捕获对象:从而通过增加引用计数向编译器指示它将来需要x)。所以x现在ARC计数为2。

当您分配xnil它时,会将引用计数减少 1。如果计数为 0,或者只有对该对象的弱引用,则该对象将被取消初始化。如果计数大于 0,并且引用是强的,则保留该对象。

如果没有明确地将捕获列表传递给闭包,它只会弱捕获对象。然后,ARC 决定一旦闭包完成,就可以安全地取消该对象。传递捕获列表向 ARC 表明闭包可能需要对象多次操作,因此进行了强引用。

再次调用someClosure()afterx = nil并查看两种情况下会发生什么。

于 2017-04-15T05:41:21.103 回答