Понимание сохранения циклов внутри функции в swift, пример из книги Мэтта Нойбурга - PullRequest
0 голосов
/ 14 февраля 2019

Рассмотрим следующий фрагмент кода:

class myDropBounceAndRollBehavior: UIDynamicBehavior {

    let v = UIView()
    init(view v: UIView) {
        self.v = v
        super.init()    
    }

    override func willMove(to anim: UIDynamicAnimator?) {
        guard let anim = anim else {return}
        let sup = self.v.superview!
        let grav = UIGravityBehavior() 

        grav.action = { [unowned self] in
            let items = anim.items(in: sup.bounds) as! [UIView]
            if items.index(of: self.v) == nil {
                anim.removeBehavior(self)
                self.v.removeFromSuperview()
            }
        }

        self.addChildBehavior(grav)
        grav.addItem(self.v)
    }
}

Здесь у нас есть класс с функцией willMove(anim:), которая имеет замыкание, которое ссылается на себя, создавая цикл сохранения.Чтобы решить эту проблему, Мэтт устанавливает self в unowned self, чтобы разорвать цикл.

Его следующий параграф говорит следующее:

Существует потенциальный (и довольно сложный) цикл сохраненияздесь: self.addChildBehavior(grav) вызывает постоянную ссылку на grav, grav имеет постоянную ссылку на grav.action, а анонимная функция, назначенная на grav.action, ссылается на self.Чтобы разорвать цикл, я объявил ссылку на self как unowned в списке захвата анонимной функции

С этим отрывком из книги я составил график ссылок для ситуации ниже

enter image description here

Таким образом, при запуске функции willMove(anim:) будет вызван вызов функции self.addChildBehavior(grav), который ссылается на grav, создав сильную ссылкук справочному экземпляру grav.но поскольку функция willMove(anim:) живет в основном потоке, функция self.addChildBehavior(grav) должна завершить до освобождения памяти кучи для willMove(anim:), поэтому self.addChildBehavior(grav) больше не имеет строгой ссылки на gravи willMove(anim:) может завершиться и память освобождается из кучи.Результат будет выглядеть следующим образом:

enter image description here

В этот момент, когда willMove(anim:) завершит выполнение, единственные оставшиеся ссылки - это unowned self ссылкак экземпляру и некоторой ссылке (например, let behavior = MyDropBounceAndRollBehaviour()), а затем, когда анонимная функция завершит выполнение, будет только behaviour ссылка на <MyDropAndBounceBehavior>

Имею ли я правильное понимание?

Ответы [ 2 ]

0 голосов
/ 14 февраля 2019

Я попытаюсь объяснить цикл владения другими словами:

self.behaviors -> grav -> action -> self
               ^ created by addChildBehavior(grav)
                       ^ created by grav.action = {
                                  ^ created by capturing

self владеет гравитационным поведением.Гравитационное поведение владеет действием, а действие захватывает (владеет) self.

Чтобы разорвать цикл владения, вы должны разорвать одно из соединений.Одно из решений состоит в том, чтобы прервать захват с помощью [weak self] или [unowned self].

. Ваши рассуждения неверны.Когда вызов функции (например, willMove) завершен, память кучи не освобождается.Память кучи при управлении памятью с подсчетом ссылок освобождается, только если у нее больше нет владельцев этой памяти.Поскольку цикл владения существует, память не может быть освобождена.Потоки на самом деле не играют никакой роли в этом.Та же самая ситуация произошла бы, если бы все вызывалось в одном и том же потоке (что на самом деле может произойти).

Я думаю, что вашей главной ошибкой является идея, что self.addChildBehavior(grav) сохраняет grav.Это не правда.Он постоянно добавляет grav к списку поведений, которыми владеет self, что означает создание сильной собственности self -> grav.

0 голосов
/ 14 февраля 2019

Может быть, это поможет:

Самостоятельно владеет grav (добавлено как поведение ребенка).Но у grav есть блок, которому принадлежит self (действие).Итак, теперь у вас есть self, которому принадлежит self, и это цикл сохранения.Вы можете использовать unowned self здесь, потому что, если self будет освобожден, блок grav тоже будет.Обычно вы используете unowned self в случаях, подобных этому, когда self владеет объектом, ссылающимся на self;в противном случае используйте weak self.

...