我有一个非常简单的代码。我有目的创建了一个与委托相关的记忆周期。尝试观察和学习如何使用Xcode的内存图。
我不明白为什么在连接部分中,Xcode表示存在 3 个连接。应该只有2。
如果我创建一个带有闭包的内存循环,那么它将显示 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!!!")
}
}
答案 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_retain
和swift_release
上设置符号断点以查看强大的保留/释放Swift ARC流量(同时打印出存储在%rdi
寄存器中的值,似乎是用于在x86上传递参数的寄存器):
,然后在调用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。