我们是否需要在Swift关闭中明确使用捕获列表?

发布于 2025-01-17 13:06:32 字数 965 浏览 1 评论 0原文

我对关闭的理解是,它将捕获所有直接引用的对象,无论对象变量是否被声明 strong 在关闭外,如果我们想要要弱捕获它们,然后我们需要明确定义捕获列表,并在该捕获列表中标记它们。

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

但是,在我的测试中,我发现,如果该变量已经在闭合外,那么我们就不需要使用捕获列表来弱捕获它。

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"

以上片段没有创建任何保留周期。这是否意味着我们不需要在捕获列表中明确捕获对象变量,如果该变量已经声明为 ,而捕获列表仅提供句法优势,而不是创建>在使用闭合之前,可变。

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.

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.

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

踏月而来 2025-01-24 13:06:32

在这个特定的例子中,是这样的,但是你需要非常小心地思考如何看待正在发生的事情。

首先,是的,这是相同的。我们可以通过生成 SIL (swiftc -emit-sil main.swift) 来判断这一点。除了 selfweakSelf 的名称不同之外,它们生成完全相同的未优化 SIL。为了使它更清楚,我将更改捕获列表中变量的名称(这只是重命名,它不会改变行为):

weakSelfweak_self

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

除了

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

比较它们

$ 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

用户评论的顺序之外self,它们是相同的。

但使用这些知识时要非常非常小心。这碰巧是正确的,因为在其他地方存在对self的强引用。在这条路上走得太远可能会导致混乱。例如,考虑以下两种方法:

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


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

它们的行为根本不同。在第一个版本中,weakObject 在创建 callback 时已被释放,因此 callback() 不执行任何操作。编译器将生成有关此问题的警告,因此在大多数情况下,这不太可能创建,但作为规则,您通常应该在捕获列表中进行弱捕获,以便它尽可能接近闭包创建发生,并赢得胜利不小心意外地被释放。

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). Except for the difference in the name of self vs weakSelf, these generate exactly the same unoptimized 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.

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. 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. 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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文