由于参考周期而导致的内存泄漏迅速

时间:2019-06-12 11:07:49

标签: swift memory-leaks

有人可以帮我避免下面的内存泄漏

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()

好像这里有一个循环引用。基本->引用和引用->本机->基本

但是我不确定如何打破这个循环。

4 个答案:

答案 0 :(得分:2)

这里有两个问题:

  1. 正如其他人指出的那样,通常,当您有两个或多个相互引用的引用类型时,您需要创建其中一个引用weak才能打破强大的引用周期。参见Resolving Strong Reference Cycles Between Class Instances

  2. 这里最严重的问题是您有无限循环。 init的{​​{1}}实例化了一个新的Base实例,但是Referenced的{​​{1}}正在创建另一个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 } } 对象,该对象恰巧实例化了一个孩子。但请注意:

  1. 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

  2. Parent仅保留对Child的{​​{1}}引用;和

  3. Child显然不会实例化新的weak,而只是使用提供给它的引用。

您也可以使用Parent Child对此进行演绎,但是想法是一样的:用Parent引用打破强引用周期并避免无限循环。


坦率地说,在这些情况下,抽象类型名称使示例不必要地引起混淆。如果通过一个实际的,真实的示例来阐明这些类型的实际含义,所有权模型无疑将变得更加不言而喻。

答案 1 :(得分:0)

将其中一个引用更改为弱:

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

希望这会有所帮助。

答案 2 :(得分:0)

您需要将Base.referNative.baseRef标记为weak才能中断循环,即将var更改为weak var

答案 3 :(得分:0)

首先,在您传递的代码段中,存在无限循环依赖的问题,如下所示:

  

基本->引用->本机->基本...

在该链的开头,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父对象,因此您将难以解决在原始问题中提到的内存泄漏。这次,为了防止内存泄漏,您必须像这样使baseRef var变弱:

weak var baseRef: Base?