Я почти уверен, что это просто запутанный код 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.