为什么Swift关闭不能捕获自我?

Jun*_*yan 6 closures automatic-ref-counting swift

我正在测试Xcode游乐场的快速关闭.

这是我的代码:

import UIKit

class A{
    var closure: ()->() = {}

    var name: String = "A"
    init() {
        self.closure = {
            self.name = self.name + " Plus"
        }
    }

    deinit {
        print(name + " is deinit")
    }
}

var a: A?
a = A()
a = nil
Run Code Online (Sandbox Code Playgroud)

正如预期的那样,a是封闭自包含的,所以a永远不会释放.

但是,当我在最后一行之前添加这一行时:

a?.closure = { a?.name = "ttt" }
Run Code Online (Sandbox Code Playgroud)

然后,我在输出窗口中发现"A is deinit",这意味着a已被释放.为什么?是不是回收参考?

要进行测试,我使用一个函数来设置闭包,代码是版本2:

import UIKit

class A{
    var closure: ()->() = {}
    func funcToSetClosure(){
        self.closure = { self.name = "BBB"}
    }
    var name: String = "A"
    init() {
        self.closure = {
            self.name = self.name + " Plus"
        }
    }

    deinit {
        print(name + " is deinit")
    }
}



var a: A?


a = A()


a?.funcToSetClosure()


a = nil
Run Code Online (Sandbox Code Playgroud)

同样,a永远不会被释放.

所以我得出了结论,当闭包由init或类中的函数设置时,它将导致循环引用,当它被设置在类的侧面时,它不会导致循环引用.我对吗?

mat*_*att 5

在这两种情况下都有保留周期。所不同的是本质的参考,而不是地方在那里closure设置。这种差异体现在打破循环所需的条件上:

  • 在“内部”情况下,闭包内部的引用是self. 当您释放对 的引用时a,这不足以打破循环,因为循环是直接自引用的。要打破这个恶性循环,你将不得不以一套a.closurenil设置前anil,而你没有这样做。

在此处输入图片说明

  • 在“外部”情况下,参考是a。只要您的a引用未设置为 ,就会有一个保留周期nil。但是你最终将其设置为nil,这足以打破这种循环。

在此处输入图片说明

(插图来自 Xcode 的内存图功能。太酷了。)