协议对保留计数有影响吗?

时间:2019-05-20 19:42:08

标签: swift memory-management memory-leaks protocols protocol-witness-table

我有一个非常简单的代码。我有目的创建了一个与委托相关的记忆周期。尝试观察和学习如何使用Xcode的内存图。

我不明白为什么在连接部分中,Xcode表示存在 3 个连接。应该只有2。

enter image description here

如果我创建一个带有闭包的内存循环,那么它将显示 2 个连接。

我与委托一起泄漏的代码:

protocol SomeDelegate {
    func didFinishSomething()
}

class Something {
    var delegate: SomeDelegate?
}

class ViewController: UIViewController, SomeDelegate {

    var x = Something()

    override func viewDidLoad() {
        super.viewDidLoad()
        print("view did load")
        x.delegate = self
    }

    func didFinishSomething() {
        print("something was finished")
    }

    deinit {
        print("dellocated!!!")
    }
}

我将该视图推送到navigationController中,然后返回。

2个委托对象的存储地址略有不同,只是+16有所不同


似乎与委托对象是协议有关。因为当我删除该协议时,它缩减为 2

class Something2 {
    var delegate: ViewController?
}

class ViewController: UIViewController {

    var x = Something2()

    override func viewDidLoad() {
        super.viewDidLoad()
        print("view did load")
        x.delegate = self
    }

    deinit {
        print("dellocated!!!")
    }
}

2 个答案:

答案 0 :(得分:1)

是的,用于实现协议类型变量的存在性容器可以生成额外的保留。有关各种实现的详细信息,请参见Understanding Swift Performance。存在容器的标头为16个字节(2个机器字)。

答案 1 :(得分:1)

我很确定这只是Xcode引起的困惑。协议值不应在正常的强引用上引起任何额外的保留,因为内存管理操作通过存在容器中存储的类型元数据转发到基础值。

这是一个最小的示例,可以在Xcode的内存图调试器中重现相同的结果:

protocol P {}

class C : P {
  var d: D?
}

class D {
  var p: P?
}

func foo() {
  let c = C()
  let d = D()
  c.d = d
  d.p = c
}

foo()

print("insert breakpoint V")

print("insert breakpoint ^")

如果在打印语句之间插入断点并查看内存图,则会看到3个连接。有趣的是,如果在分配c.d之后分配d.p,则会看到2个连接的正确结果。

但是,如果我们在swift_retainswift_release上设置符号断点以查看强大的保留/释放Swift ARC流量(同时打印出存储在%rdi寄存器中的值,似乎是用于在x86上传递参数的寄存器):

enter image description here

,然后在调用foo()之后立即插入一个断点,我们可以看到,在这两种情况下,实例各自保留+2并释放-2(请注意,它们以+1保留进入世界,从而保持分配状态):

swift_retain 1
     rdi = 0x000000010070fcd0
swift_retain 2
     rdi = 0x000000010070fcd0
swift_release 1
     rdi = 0x0000000000000000
swift_release 2
     rdi = 0x000000010070fcd0
swift_retain 3
     rdi = 0x00000001007084e0
swift_retain 4
     rdi = 0x00000001007084e0
swift_release 3
     rdi = 0x00000001007084e0
swift_release 4
     rdi = 0x000000010070fcd0
swift_release 5
     rdi = 0x00000001007084e0

所以这里看起来Xcode出了问题,而不是Swift。