如何知道闭包是否归班级所有?

Rob*_*hwa 5 closures swift

我很难弄清楚如何确保何时在封闭体中使用[弱自我]/[无主自我].在下面显示的两个场景中,根据我的说法,它取决于B类是否拥有传递的闭包.

现在如果隐藏了B类的实现,我不确定如何决定使用[弱自我]/[无主自我].

有人可以帮我理解你将如何决定?

/******** Scenario 1 **********/

class A {
    var b:B?
    let p = "Some Property of A"

    init() {
        print("Init of A")

        self.b = B(closure: { (number) -> Void in
            print(self.p)       // capturing self but still no need to write [weak/unowned self]
            print(number)
        })
    }

    deinit {
        print("Deinit of A")
    }
}

// Suppose this is a library class whose implementation is hidden
class B {
    init(closure:(Int->Void)) {
        print("Init of B")
        // ... do some work here
        closure(20)
    }
    deinit {
        print("Deinit of B")
    }
}

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

输出:

//    Init of A
//    Init of B
//    Some Property of A
//    20
//    Deinit of A
//    Deinit of B
Run Code Online (Sandbox Code Playgroud)

现在第二个场景将导致参考周期.

/******** Scenario 2 **********/

class A {
    var b:B?
    let p = "Some Property of A"
    init() {
        print("Init of A")

        self.b = B(closure: { (number) -> Void in
            print(self.p)       // capturing self but NEED to write [weak/unowned self]
            print(number)
        })
    }

    deinit {
        print("Deinit of A")
    }
}

// Suppose this is a library class whose implementation is hidden
class B {
    let closure:(Int->Void)

    init(closure:(Int->Void)) {
        print("Init of B")
        self.closure = closure     //class B owns the closure here
        f()
    }

    func f() {
        self.closure(20)
    }
    deinit {
        print("Deinit of B")
    }
}

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

nhg*_*rif 6

"拥有"的想法可能是错误的术语.Objective-C&Swift使用ARC来管理内存.这是不同类型的引用的系统(strong,weak,和unowned).并且重要的是要注意,除非引用被标记为是weakunowned,它是strong.


那么,让我们首先看看你的第一个例子,并注意你的参考.

在您的类声明下面,我们有以下代码:

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

因为a没有标记为weakor unowned,所以它是我们在同一行中创建的对象的强引用a.因此,a是对该对象的强引用,直到a不再指向该对象(在这种情况下发生在第二行).而且正如我们所知,强有力的参考可以防止发生重新分配.

现在让我们深入研究A的init方法,我们实际上是在这一行中调用的.

init() {
    print("Init of A")

    self.b = B(closure: { (number) -> Void in
        print(self.p)
        print(number)
    })
}
Run Code Online (Sandbox Code Playgroud)

A的第一件事init是印刷"Init of A",这是我们看到的第一件看印刷的东西.

它接下来要做的是为它的b属性赋值.它的b属性也没有标记为weakunowned,所以这是一个强大的参考.

它所分配的值b是一个新构造的B类实例,它解释了我们看到的第二行打印:"Init of B",因为这B是初始化程序的第一行.

但是B,初始化程序需要关闭.这是我们通过的关闭:

{ (number) -> Void in
    print(self.p)       // capturing self but still no need to write [weak/unowned self]
    print(number)
}
Run Code Online (Sandbox Code Playgroud)

这个块绝对有一个强引用self(在这种情况下,之前a打印的实例)"Init of A".

那么,为什么没有保留周期呢?好吧,让我们来看看B初始化程序.它与封闭有什么关系?

class B {
    init(closure:(Int->Void)) {
        print("Init of B")
        // ... do some work here
        closure(20)
    }
    deinit {
        print("Deinit of B")
    }
}
Run Code Online (Sandbox Code Playgroud)

因此,当我们实例化一个实例时B,它会触发闭包,然后忘记它.我们传递的封闭没有强烈的参考.

那么,让我们来看看我们的参考文献:

 global --strong--> a
closure --strong--> a
      a --strong--> b
Run Code Online (Sandbox Code Playgroud)

因此,b只要a存在,将继续有强有力的参考并继续存在,并保持其强有力的参考.并且a只要在您的全局引用和闭包之间至少有一件事继续存在并且保持对它的强烈引用,它将继续存在.

但请注意,没有什么能够强烈提及关闭.至少,不超出其使用的任何方法的范围.

B初始化保持着强劲的引用传递给它只有等到初始化结束关闭.

所以,当我们写这行时:

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

A()返回时,变量a仍然是唯一的强引用a,并且a仍然是唯一的强引用b.有可能创建参考周期的闭合不再存在.然后,当我们设置a为时nil,我们杀死了我们强烈的引用a.

a = nil
Run Code Online (Sandbox Code Playgroud)

所以,a解除分配.在发生这种情况时,仍然没有强烈的引用b,因此它也会解除分配.


你的第二个例子是不同的.在第二个例子中,实现A保持不变,但实现B已经改变. B现在有了这个closure属性,它可以强有力地引用任何传递给B初始化程序的闭包.

所以现在,我们的引用看起来像这样:

 global --strong--> a
closure --strong--> a
      a --strong--> b
      b --strong--> closure
Run Code Online (Sandbox Code Playgroud)

所以你可以看到,即使我们打破了全局引用a,仍然存在一个保留周期:

a --> b --> closure --> a
Run Code Online (Sandbox Code Playgroud)

如果我们不使用[weak self][unowned self],封闭绝对有强烈的参考self.是否创建保留周期取决于对闭包的强引用.

为了确定第三方库,首先检查源代码或文档.在Apple之外,我目前不知道如何使用我们无法调查的私有实现来分发Swift库,并且Apple的代码都有详细记录.但假设最坏的情况,假设我们真的没有办法,那么将你传递给第三方库的任何封闭视为库将拥有强大的引用.

即使这并不一定意味着我们必须始终使用[weak self][unowned self]在我们的封闭中.

如果我们在上面的例子中注意到,有多种方法可以打破保留周期.记住它的样子:

a -> b -> closure -> a
Run Code Online (Sandbox Code Playgroud)

因此,使用[weak self][unowned self]将阻止保留周期,因为它将消除闭包的引用a.但即使关闭仍然强烈提及a,请注意,如果我们打破了周期突破a的强烈参考b.什么都没有强大的参考,b所以b将解除分配.这将使得没有任何东西强烈提及关闭,允许关闭解除分配,然后,没有(至少在这个周期内)保持a活力.

所以,如果我们传递一个类似这样的闭包......

{ (number) in 
    print(self.p)
    print(number)
    self.b = nil
}
Run Code Online (Sandbox Code Playgroud)

这第三条线恰好打破了这个循环,因为现在self不再有强烈的参考,b它强有力地提到了封闭性,而这种封闭具有强烈的参考作用self.