Влияют ли протоколы на количество записей? - PullRequest
2 голосов
/ 20 мая 2019

У меня очень простой код.Я НАЗНАЧЕНО создаю цикл памяти с делегатом.Попытка наблюдать и научиться использовать График Памяти 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 ]

1 голос
/ 22 мая 2019

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

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.

1 голос
/ 20 мая 2019

Да, экзистенциальный контейнер, который используется для реализации переменных типа протокола, может генерировать дополнительное сохранение.См. Понимание производительности Swift для различных деталей реализации.16 байтов (2 машинных слова) - заголовок экзистенциального контейнера.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...