简体   繁体   English

我们是否需要在 swift 闭包中为弱变量显式使用捕获列表?

[英]Do we need to explicitly use capture list for weak variables in swift closure?

My understanding for closure was, it will capture all directly referenced objects strongly irrespective of whether the object variable was declared weak or strong outside the closure and, if we want to capture them weakly, then we need to explicitly define a capture list and mark them weak in that capture list.我对闭包的理解是,它会强捕获所有直接引用的对象,而不管 object 变量在闭包外是weak声明还是strong声明,如果我们弱捕获它们,那么我们需要显式定义一个捕获列表并标记它们在那个捕获列表中weak

obj?.callback = { [weak obj] in
    obj?.perform()
}

However in my test, I found that if the variable is already weak outside the closure, then we don't need to use capture list to capture it weakly.但是在我的测试中,我发现如果这个变量在闭包外已经是weak了,那么我们就不需要使用捕获列表来弱捕获它了。

class Foo {
    var callback: (() -> ())?
    
    init() {
        weak var weakSelf = self
        callback = {
            weakSelf?.perform()
        }
//        is above equivalent to below in terms of memory management?
//        callback = { [weak self] in
//            self?.perform()
//        }
    }
    
    func perform() {
        print("perform")
    }
    
    deinit {
        print("deinit")
    }
}

let foo = Foo() // prints "deinit foo"

The above snippet is not creating any retain cycle.上面的代码片段没有创建任何保留周期。 Does this mean we don't need to explicitly capture an object variable weakly in capture list if the variable is already declared weak and capture list just provides syntactical advantage over creating a weak variable before using them inside closure.这是否意味着我们不需要在捕获列表中显式地捕获一个 object 变量,如果该变量已经被声明为weak并且捕获列表只是提供了语法优势,而不是在闭包中使用它们之前创建一个weak变量。

Sort of, in this specific example, but you need to be very careful about how you think about what's happening.有点,在这个具体的例子中,但你需要非常小心你如何看待正在发生的事情。

First, yes, this is identical.首先,是的,这是相同的。 We can tell that by generating the SIL ( swiftc -emit-sil main.swift ).我们可以通过生成 SIL ( swiftc -emit-sil main.swift ) 来判断。 Except for the difference in the name of self vs weakSelf , these generate exactly the same unoptimized SIL.除了selfweakSelf的名称不同外,它们生成完全相同的未优化 SIL。 In order to make it even clearer, I'll change the name of the variable in the capture list (this is just a renaming, it doesn't change the behavior):为了让它更清楚,我将更改捕获列表中变量的名称(这只是重命名,它不会改变行为):

weakSelf弱小的自我

    weak var weakSelf = self
    callback = {
        weakSelf?.perform()
    }

weak_self弱者

    callback = { [weak weakSelf = self] in
        weakSelf?.perform()
    }

Compare them比较它们

$ swiftc -emit-sil weakSelf.swift > weakSelf.sil
$ swiftc -emit-sil weak_self.swift > weak_self.sil
$ diff -c weakSelf.sil weak_self.sil

*** weakSelf.sil    2022-03-27 10:58:13.000000000 -0400
--- weak_self.sil   2022-03-27 11:01:22.000000000 -0400
***************
*** 102,108 ****

  // Foo.init()
  sil hidden @$s4main3FooCACycfc : $@convention(method) (@owned Foo) -> @owned Foo {
! // %0 "self"                                      // users: %15, %8, %7, %2, %22, %1
  bb0(%0 : $Foo):
    debug_value %0 : $Foo, let, name "self", argno 1, implicit // id: %1
    %2 = ref_element_addr %0 : $Foo, #Foo.callback  // user: %4
--- 102,108 ----

  // Foo.init()
  sil hidden @$s4main3FooCACycfc : $@convention(method) (@owned Foo) -> @owned Foo {
! // %0 "self"                                      // users: %8, %7, %15, %2, %22, %1
  bb0(%0 : $Foo):
    debug_value %0 : $Foo, let, name "self", argno 1, implicit // id: %1
    %2 = ref_element_addr %0 : $Foo, #Foo.callback  // user: %4

Except for the order of the users comment for self , they're identical.除了用户对self的评论顺序外,它们是相同的。

But be very, very careful with what you do with this knowledge.但是要非常、非常小心地使用这些知识。 This happens to be true because there exists a strong reference to self elsewhere.恰好是真的,因为在其他地方存在对self的强烈参考。 Going down this road too far can lead to confusion.在这条路上走得太远会导致混乱。 For example, consider these two approaches:例如,考虑这两种方法:

// Version 1
weak var weakObject = Object()
let callback = {
    weakObject?.run()
}
callback()


// Version 2
var weakObject = Object()
let callback = { [weak weakObject] in
    weakObject?.run()
}
callback()

These do not behave the same at all.这些行为根本不一样。 In the first version, weakObject is already released by the time callback is created, so callback() does nothing.在第一个版本中, weakObject在创建callback时已经被释放,所以callback()什么都不做。 The compiler will generate a warning about this, so in most cases this is an unlikely bug to create, but as a rule you should generally do weak captures in the capture list so that it occurs as close to the closure creation as possible, and won't accidentally be released unexpectedly.编译器会生成一个警告,所以在大多数情况下,这是一个不太可能产生的错误,但通常你应该在捕获列表中进行弱捕获,以便它尽可能接近闭包创建,并且赢了不小心被意外释放。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM