утечка памяти с быстрым из-за опорный цикл - PullRequest
2 голосов
/ 12 июня 2019

Может кто-нибудь помочь мне избежать утечки памяти ниже

class Base {
     var refer: Referenced?
    init() {
        self.refer = Referenced()
    }
    deinit {
        print("deinit called")
    }
}



class Referenced {
    var native: Native
    init(){
       native = Native.init(baseRef: Base())
    }


}
struct Native {
    var baseRef: Base
}
func testMe () {
    var base:Base? = Base()
    base?.refer = nil
    base = nil
}

testMe()

Похоже, здесь есть циклическая ссылка.Base -> Referenced and Referenced -> Native -> Base

Но я не уверен, как разорвать этот цикл.

Ответы [ 4 ]

2 голосов
/ 12 июня 2019

Здесь есть два вопроса:

  1. Как уже отмечали другие, в общем, когда у вас есть два или более ссылочных типа, ссылающихся друг на друга, вам нужно сделать одну из этих ссылок weak, чтобы разорвать цикл сильных ссылок. См. Разрешение сильных эталонных циклов между экземплярами класса .

  2. Более серьезная проблема в том, что у вас бесконечный цикл. init из Base создает новый экземпляр Referenced, но init из Referenced создает другой экземпляр Base (который передается в экземпляр Native). Этот новый экземпляр Base затем создаст другой экземпляр Referenced, повторяя процесс, и будет продолжать это делать, пока не исчерпает память / стек.

    Добавьте операторы print в различные методы init, и вы увидите этот бесконечный цикл в действии.

Вам следует вернуться к этой модели, определить график «владения». Родительские объекты могут сохранять надежные ссылки на дочерние объекты (и, возможно, создавать экземпляры дочерних объектов, если это необходимо), но дочерние объекты должны сохранять только ссылки weak на свои родительские объекты (и, скорее всего, не создавать экземпляры новых родительских объектов).

Например, вы можете сделать:

class Parent {
    var child: Child?

    init() {
        self.child = Child(parent: self)
    }

    deinit {
        print("deinit called")
    }
}

class Child {
    weak var parent: Parent?

    init(parent: Parent) {
        self.parent = parent
    }
}

И

func testMe() {
    var parent: Parent? = Parent()
    parent = nil                   // in this case, this is unnecessary because `parent` is a local variable, but this is here for illustrative purposes; but note that we don’t have to `nil` the `child`
}

Здесь вы создаете экземпляр объекта Parent, который создается для создания экземпляра дочернего элемента. Но обратите внимание, что:

  1. Parent предоставляет себя в качестве параметра для Child;

  2. Child поддерживает только ссылку weak на Parent; и

  3. Очевидно, что Child не создает экземпляр нового Parent, а просто использует ссылку, предоставленную ему.

Вы также можете сделать это с вашим Native struct, но идея будет та же: разорвать цикл сильных ссылок с помощью ссылки weak и избежать бесконечного цикла.


Честно говоря, в этих случаях абстрактные имена типов приводят к излишней путанице в примерах. Если вы поясните, что на самом деле представляют эти различные типы, на практическом примере из реальной жизни, модель собственности, несомненно, станет более очевидной.

0 голосов
/ 12 июня 2019

Вам нужно пометить либо Base.refer, либо Native.baseRef как weak, чтобы разорвать цикл, т. Е. Изменить var на weak var.

0 голосов
/ 12 июня 2019

Прежде всего, во фрагменте кода, который вы передали, у вас есть проблема с бесконечной циклической зависимостью, подобной этой:

Base -> Referenced -> Native -> Base ...

Экземпляр структуры Native не ссылается на экземпляр Base в начале этой цепочки, он всегда создает новый, и это поведение бесконечно.Просто скопируйте свой код на игровую площадку и попробуйте запустить его.Вы получите ошибку "Execution was interrupted".Итак, ваш код даже не запускается.Как я полагаю, вы пытаетесь добиться, чтобы структура Native содержала ссылку на первый экземпляр Base.Вот правильное решение, в котором мы передаем ссылку из Base в Referenced и Native, которая будет содержать ссылку:

class Base {
    var refer: Referenced?
    init() {
        self.refer = Referenced(base: self)
    }
    deinit {
        print("deinit called")
    }
}

class Referenced {
    var native: Native
    init(base: Base){
        native = Native.init(baseRef: base)
    }
}

struct Native {
    var baseRef: Base
}

А теперь, когда Native ссылается на Base parent, вы будете бороться с утечкой памяти, упомянутой в исходном вопросе.И на этот раз, чтобы предотвратить утечку памяти, вы должны сделать ваш baseRef var слабым, вот так:

weak var baseRef: Base?
0 голосов
/ 12 июня 2019

Измените одну из ссылок на слабую, например так:

class Base {
    weak var refer: Referenced?
    init() {
        self.refer = Referenced()
    }
    deinit {
        print("deinit called")
    }
}

Надеюсь, это поможет.

...