这是一个强大的参考周期还是内存泄漏?

时间:2016-05-22 06:21:50

标签: swift memory-leaks automatic-ref-counting strong-references

我经常看到的关于强引用循环的示例涉及两个具有指向彼此的属性的类。但是,如果只有一个类具有指向另一个实例的属性,如下所示:

class ClassA {
    var classB: ClassB? = nil
}

class ClassB {

}

然后我创建我的实例:

var myClassA = ClassA()
var myClassB = ClassB() //Reference count 1
myClassA.classB = myClassB //Reference count 2

// Now deallocate
myClassB = nil  //Reference count 1
myClassA = nil

由于我已取消分配myClassB,引用计数为1. myClassA.classB的引用计数发生了什么变化?它永远不会达到零,因为我从未myClassA.classB = nil或使用deinit来执行此操作。这是我自myClassA = nil完成后隐式完成的吗?

这可以归类为强参考周期吗?我会想象它至少是一个内存泄漏,这是真的吗?

1 个答案:

答案 0 :(得分:2)

正如@ozgur,@ jtbandes,@ Avi和@Rob在评论中解释的那样,没有强大的参考周期或泄漏。

以下是基于@ Rob评论的示例,您可以在Playground中运行:

class ClassA {
    var classB: ClassB?

    deinit {
        print("ClassA deallocated")
    }
}

class ClassB {
    deinit {
        print("ClassB deallocated")
    }
}

class Tester {
    func test() {
        var myClassA: ClassA! = ClassA()
        var myClassB: ClassB! = ClassB() //Reference count 1
        myClassA.classB = myClassB //Reference count 2

        // Now deallocate
        print("setting myClassB to nil")
        myClassB = nil  //Reference count 1
        print("setting myClassA to nil")
        myClassA = nil
        print("all done")
    }
}

// Create `Tester` object and call `test`:

Tester().test()

输出:

setting myClassB to nil
setting myClassA to nil
ClassA deallocated
ClassB deallocated
all done

需要注意的是,即使您先将myClassB设置为nil,也会先释放myClassA myClassA。取消myClassB后,ARC会释放myClassB的最终引用,然后释放ClassB

要展示强大的参考周期,请ClassA保留对class ClassA { var classB: ClassB? deinit { print("ClassA deallocated") } } class ClassB { var classA: ClassA? deinit { print("ClassB deallocated") } } class Tester { func test() { var myClassA:ClassA! = ClassA() var myClassB:ClassB! = ClassB() //Reference count 1 myClassA.classB = myClassB //Reference count 2 myClassB.classA = myClassA // Now deallocate print("setting myClassB to nil") myClassB = nil //Reference count 1 print("setting myClassA to nil") myClassA = nil print("all done") } } Tester().test() 的强引用:

setting myClassB to nil
setting myClassA to nil
all done

输出:

classB

如果对象都包含对另一个的强引用,则它们都不会被释放。要打破此强引用周期,请将classAweak属性之一声明为weak var classB: ClassB。您选择哪一个会影响对象被释放的顺序:

如果您在ClassA中声明setting myClassB to nil ClassB deallocated setting myClassA to nil ClassA deallocated all done

输出:

weak var classA: ClassA in ClassB

相反,如果您声明setting myClassB to nil setting myClassA to nil ClassA deallocated ClassB deallocated all done

输出:

struct ImageFrame {
   Image image;
};
struct Image {
   ImageFrame imageFrame;
};