在涉及 NSHashTable 的特殊情况下不调用 deinit
Deinit not called in special circumstance that involves NSHashTable
我发现了一些奇特有趣的东西,很想听听任何人的意见。因此,首先让我们采用 class:
的定义
class TestClass:NSObject {
var s1 = NSHashTable<TestClass>(options: .weakMemory)
func doit(bla:TestClass) {
s1.add(bla)
bla.s1.add(self)
}
deinit {
print("Deinit")
}
}
现在让我们考虑以下问题:
var t1:TestClass? = TestClass()
var t2:TestClass? = TestClass()
如果我们执行了以下 deinit 调用:
t1?.s1.add(t2!)
t2?.s1.add(t1!)
t1 = nil // This will result in deinit being called
现在让我们做同样的事情,但是通过调用 doit() 方法
t1?.doit(bla:t2!)
t1 = nil // Deinit doesn't get called for some reason
这里的问题是为什么在这种情况下不调用deinit?这有什么不同,因为它基本上具有与第一种方法相同的引用分配?
我很乐意听取任何人的意见。
deinit
未被调用,因为您创建了 reference cycle.
首先,您要创建从 self
到 bla
的 scstrong 引用:s1.add(bla)
其次,您创建了从 bla
到 self
的强引用:bla.s1.add(self)
现在它们都相互引用了,所以如果您只是取消其中之一,它们就不会 deinit
。
我修改了你的 TestClass
以删除引用循环:
class TestClass:NSObject {
weak var s1 = NSHashTable<TestClass>(options: .weakMemory)
func doit(bla:TestClass) {
s1?.add(bla)
bla.s1?.add(self)
}
deinit {
print("Deinit")
}
}
现在您的第二个调用将正确触发 deinit
。
像往常一样,问题是您正试图在操场上测试这个。不。游乐场是魔鬼的杰作。
在实际的应用项目中测试,你会看到deinit
被调用了。
示例(iOS,但 macOS 中的等效项也可以):
import UIKit
class TestClass:NSObject {
var s1 = NSHashTable<TestClass>(options: .weakMemory)
func doit(bla:TestClass) {
s1.add(bla)
bla.s1.add(self)
}
deinit {
print("Deinit")
}
}
class ViewController: UIViewController {
var t1:TestClass? = TestClass()
var t2:TestClass? = TestClass()
override func viewDidLoad() {
super.viewDidLoad()
t1?.doit(bla:t2!)
t1 = nil // --> "Deinit"
print(t2?.s1) // --> it's empty
}
}
我发现了一些奇特有趣的东西,很想听听任何人的意见。因此,首先让我们采用 class:
的定义class TestClass:NSObject {
var s1 = NSHashTable<TestClass>(options: .weakMemory)
func doit(bla:TestClass) {
s1.add(bla)
bla.s1.add(self)
}
deinit {
print("Deinit")
}
}
现在让我们考虑以下问题:
var t1:TestClass? = TestClass()
var t2:TestClass? = TestClass()
如果我们执行了以下 deinit 调用:
t1?.s1.add(t2!)
t2?.s1.add(t1!)
t1 = nil // This will result in deinit being called
现在让我们做同样的事情,但是通过调用 doit() 方法
t1?.doit(bla:t2!)
t1 = nil // Deinit doesn't get called for some reason
这里的问题是为什么在这种情况下不调用deinit?这有什么不同,因为它基本上具有与第一种方法相同的引用分配?
我很乐意听取任何人的意见。
deinit
未被调用,因为您创建了 reference cycle.
首先,您要创建从 self
到 bla
的 scstrong 引用:s1.add(bla)
其次,您创建了从 bla
到 self
的强引用:bla.s1.add(self)
现在它们都相互引用了,所以如果您只是取消其中之一,它们就不会 deinit
。
我修改了你的 TestClass
以删除引用循环:
class TestClass:NSObject {
weak var s1 = NSHashTable<TestClass>(options: .weakMemory)
func doit(bla:TestClass) {
s1?.add(bla)
bla.s1?.add(self)
}
deinit {
print("Deinit")
}
}
现在您的第二个调用将正确触发 deinit
。
像往常一样,问题是您正试图在操场上测试这个。不。游乐场是魔鬼的杰作。
在实际的应用项目中测试,你会看到deinit
被调用了。
示例(iOS,但 macOS 中的等效项也可以):
import UIKit
class TestClass:NSObject {
var s1 = NSHashTable<TestClass>(options: .weakMemory)
func doit(bla:TestClass) {
s1.add(bla)
bla.s1.add(self)
}
deinit {
print("Deinit")
}
}
class ViewController: UIViewController {
var t1:TestClass? = TestClass()
var t2:TestClass? = TestClass()
override func viewDidLoad() {
super.viewDidLoad()
t1?.doit(bla:t2!)
t1 = nil // --> "Deinit"
print(t2?.s1) // --> it's empty
}
}