从`deinit`中走私`self`

Posted

技术标签:

【中文标题】从`deinit`中走私`self`【英文标题】:Smuggling `self` out of a `deinit` 【发布时间】:2020-01-14 16:01:03 【问题描述】:

如果我通过将self 从我的deinit 中偷运出来,将其分配给某个外部强引用,会发生什么情况?下面这段代码显然格式不正确:

class C: CustomStringConvertible 
    let s = "abc"

    var description: String 
        return "C(id: \(ObjectIdentifier(self)), s: \(s))"
    

    deinit 
        print("deinit")
        globalObject = self
    


var globalObject: C!

do 
    let localObject = C()
    print("localObject: \(localObject)")
    print("end of `do`")


print("globalObject: \(globalObject!)")

您不能只是从deinit 的中间“改变主意”来取消对象的初始化。但有趣的是,这段代码是不确定的,它有时会成功完成,打印:

localObject: C(id: ObjectIdentifier(0x00007f9063f00960), s: abc)
end of `do`
deinit
globalObject: C(id: ObjectIdentifier(0x00007f9063f00960), s: abc)

我正在使用 Code Runner 运行它,它只是使用 swiftc 运行单个文件 Swift 脚本。所以这里没有意外的 Playground 拥有的引用。

不确定性从何而来?

【问题讨论】:

我在 Playground 和 Repl.It 上运行了十次,每次崩溃。您需要运行多少次才能成功完成? Idk,大约十分之一。swiftc --version 给出Apple Swift version 5.1 (swiftlang-1100.0.212.5 clang-1100.0.28.2) Target: x86_64-apple-darwin19.0.0 我用的是5.0.1,又跑了几次,还是不行。似乎是特定于 5.1 那么... forums.swift.org/t/retain-self-in-deinit/6365 和 ***.com/questions/49117750/… @J.Doe 我知道这是不合法的。我只是不知道为什么这不会总是崩溃。 【参考方案1】:

这不是答案,但评论太长了:

有趣的是,我只是附加了:

for _ in 1...1000000 
    print("intermediate: \(globalObject!)")

print("globalObject: \(globalObject!)")

然后:

swiftc test.swift
for ((i = 0; i < 10000; i++)); do
    ./test  | fgrep globalObject
done

我得到的是(这 10000 次运行中只有一次):

致命错误:对象被保留太多次a(44854,0x10a92f5c0) malloc: 已释放对象 0x7fcc1ec02b98 的校验和不正确:可能在被释放后修改。

损坏值:0x7ffffffe00000000

test(44854,0x10a92f5c0) malloc: *** 在 malloc_error_break 中设置断点进行调试

globalObject: C(id: ObjectIdentifier(0x00007f7f452006c0), s: abc)

test(61212,0x114d7e5c0) malloc: 已释放对象 0x7fe063e00008 的校验和不正确:释放后可能已修改。

损坏值:0x4ffffffe00000000

test(61212,0x114d7e5c0) malloc: *** 在 malloc_error_break 中设置断点进行调试

下一次运行:

test(7016,0x10682d5c0) malloc: tiny_free_list_remove_ptr: 内部不变量损坏(prev 的下一个 ptr):ptr=0x7fb2e0805fd0, prev_next=0x7f92e0805fd0

这似乎是某种竞争条件,但我不知道是谁在这里产生线程。 顺便说一句:

swiftc -version
Apple Swift version 5.0.1 (swiftlang-1001.0.82.4 clang-1001.0.46.5)
Target: x86_64-apple-darwin18.7.0

【讨论】:

以上是关于从`deinit`中走私`self`的主要内容,如果未能解决你的问题,请参考以下文章

Deinit / 无效定时器

从内存中删除 UIView 时,快速 deinit 方法不起作用

是否应该重写 deinit 以删除 Swift 中的观察者?

从 NSManagedObject deinit 的上下文中删除关系和对象

当这行代码执行时 deinit() 停止调用

调用了 Deinit 但对象仍在内存中