swift - 同步调度的保留计数如何工作?

标签 swift multithreading memory-management grand-central-dispatch retaincount

我试图解释对象的所有权以及 GCD 是如何工作的。
这些是我学到的东西:

  • 一个函数将增加其针对
  • 调用的对象的保留计数
  • 一个调度 block ,除非它捕获 self弱会增加计数。
  • 在执行调度的 block 后,它会释放捕获的对象,因此保留计数为 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
    

    最佳答案

    几个想法:

  • 如果您对 ARC 在幕后保留和释放的确切位置有疑问,只需在“同步后内部函数”之后添加断点,运行它,当它停止时使用“调试”»“调试工作流”»“始终显示反汇编”你可以看到程序集,准确地看到发生了什么。我还建议使用发布/优化构建来执行此操作。

    查看程序集,版本在您的 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 进入视频,他们在视频中讨论了编译器在哪里插入 retainrelease调用,以及它在 Swift 4.2 中的变化。

    在 Swift 4.1 中,它使用“Owned”调用约定,调用者会在调用函数之前保留对象,而被调用函数负责在返回之前执行释放。

    在 4.2 中(如下面的 WWDC 屏幕快照所示),他们实现了“保证”调用约定,消除了许多多余的保留和释放调用:

    enter image description here

    这至少在优化的构建中导致更高效和更紧凑的代码。所以,做一个发布构建并查看程序集,你会看到它在起作用。
  • 现在,我们来到您问题的根源,为什么 GCD sync函数的行为与其他场景不同(例如,它的释放调用插入到与其他具有非转义闭包的场景不同的位置)。

    似乎这可能与 GCD 独有的优化有关 sync .具体来说,当您同步调度到某个后台队列时,而不是停止当前线程,然后在指定队列的工作线程之一上运行代码,编译器足够聪明地确定当前线程将处于空闲状态并且它将如果可以的话,只需在当前线程上运行分派(dispatch)的代码。我很容易想象这个 GCD sync优化,可能会在关于编译器在何处插入发布调用的逻辑中引入皱纹。

  • 恕我直言,发布是在方法结束而不是在关闭结束时完成的事实有点学术问题。我假设他们有充分的理由(或至少是实际的理由),将其推迟到函数的末尾。重要的是当您从 foo 返回时,保留计数是应该的。

    关于swift - 同步调度的保留计数如何工作?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58378448/

    相关文章:

    swift - 多重变换 UIView

    ios - 有没有办法可以更改消息套件中整个输入栏的背景颜色?

    java - RxJava 调度程序始终与 sleep 在同一线程中工作

    c++ - 可以从 Windows DLL 中的全局变量创建/销毁 std::threads 吗?

    java - 由于正在运行的线程,应用程序在关闭 fragment 时崩溃

    c++ - 在单线程应用程序中在堆栈上分配大量内存是否可以?

    ios - 时间增加一天后不会发送本地通知

    swift - 表格 View 单元格被导航栏重叠

    android - Android 上 App 之间的切换会发生什么?

    android:largeHeap ="true"约定?