我试图解释对象的所有权以及 GCD 是如何工作的。
这些是我学到的东西:
self
弱会增加计数。 self
应该减少。但这不是我在这里看到的。这是为什么? class C {
var name = "Adam"
func foo () {
print("inside func before sync", CFGetRetainCount(self)) // 3
DispatchQueue.global().sync {
print("inside func inside sync", CFGetRetainCount(self)) // 4
}
sleep(2)
print("inside func after sync", CFGetRetainCount(self)) // 4 ?????? I thought this would go back to 3
}
}
用法:
var c: C? = C()
print("before func call", CFGetRetainCount(c)) // 2
c?.foo()
print("after func call", CFGetRetainCount(c)) // 2
最佳答案
几个想法:
查看程序集,版本在您的
foo
末尾。方法。 DispatchQueue.global().sync
打电话成为 async
,您会看到预期的行为。此外,不出所料,如果您执行功能分解,移动 GCD
sync
调用一个单独的函数,您将再次看到您所期望的行为。 a function will increase the retain count of the object its calling against
为了澄清发生了什么,我建议您引用 WWDC 2018 What’s New in Swift ,大约 12:43 进入视频,他们在视频中讨论了编译器在哪里插入
retain
和 release
调用,以及它在 Swift 4.2 中的变化。在 Swift 4.1 中,它使用“Owned”调用约定,调用者会在调用函数之前保留对象,而被调用函数负责在返回之前执行释放。
在 4.2 中(如下面的 WWDC 屏幕快照所示),他们实现了“保证”调用约定,消除了许多多余的保留和释放调用:
这至少在优化的构建中导致更高效和更紧凑的代码。所以,做一个发布构建并查看程序集,你会看到它在起作用。
sync
函数的行为与其他场景不同(例如,它的释放调用插入到与其他具有非转义闭包的场景不同的位置)。似乎这可能与 GCD 独有的优化有关
sync
.具体来说,当您同步调度到某个后台队列时,而不是停止当前线程,然后在指定队列的工作线程之一上运行代码,编译器足够聪明地确定当前线程将处于空闲状态并且它将如果可以的话,只需在当前线程上运行分派(dispatch)的代码。我很容易想象这个 GCD sync
优化,可能会在关于编译器在何处插入发布调用的逻辑中引入皱纹。 恕我直言,发布是在方法结束而不是在关闭结束时完成的事实有点学术问题。我假设他们有充分的理由(或至少是实际的理由),将其推迟到函数的末尾。重要的是当您从
foo
返回时,保留计数是应该的。
关于swift - 同步调度的保留计数如何工作?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58378448/