如何在 Swift 中演示僵尸对象?

Hon*_*ney 6 memory-management nszombie dangling-pointer swift

我已阅读如何在 Xcode Instruments 中演示内存泄漏和僵尸对象?但这是针对 Objective-c 的。这些步骤不适用。

通过阅读这里,我了解到僵尸是以下物体:

  • 解除分配
  • 但某些指针仍在尝试指向它们并向它们发送消息。

不太确定这与访问已释放的对象有何不同。

我的意思是在 Swift 中你可以这样做:

var person : Person? = Person(name: "John")
person = nil
print(person!.name)
Run Code Online (Sandbox Code Playgroud)

人员被解除分配了吗?是的!

我们是否试图指出这一点?是的!

那么有人可以分享导致创建悬空指针的最常见错误吗?

Rob*_*ier 6

这不是悬空指针或僵尸指针。当你使用时,!你会说“如果这是零,那么崩溃”。你不应该将其视为personSwift 中的指针。这是一个价值。该值可能是.some(T),也可能是.none(也称为nil)。这些都不是悬空的。它们只是两个不同的显式值。Swift 的nil空指针与其他语言中的空指针完全不同。只有当您明确要求它时,它才会像空指针一样崩溃。

要创建僵尸,您需要使用类似的东西Unmanaged。这在 Swift 中极为罕见。

  • @Honey 引用是通过下面的指针实现的,但话又说回来,所有分支语句(函数调用、“return”、“if”/“else”)、数组、闭包和许多其他实体也是如此。指针支持“许多”事物,但这并不意味着这些事物等同于指针。 (2认同)

Cri*_*tik 6

以下是不到 15 行代码的僵尸攻击:

class Parent { }

class Child {
    unowned var parent: Parent // every child needs a parent

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

var parent: Parent? = Parent()
let child = Child(parent: parent!) // let's pretend the forced unwrap didn't happen
parent = nil // let's deallocate this bad parent
print(child.parent) // BOOM!!!, crash
Run Code Online (Sandbox Code Playgroud)

这段代码中发生的情况是,Child持有对 的无主引用,一旦被释放Parent,该引用就会变得无效。Parent该引用包含一个指向已去世的父级 (RIP) 的指针,访问该指针会导致崩溃,并显示类似于以下内容的消息:

致命错误:尝试读取无主引用,但对象 0x1018362d0 已被释放2018-10-29 20:18:39.423114+0200 MyApp[35825:611433] 致命错误:尝试读取无主引用,但对象 0x1018362d0 已被释放

注意该代码无法在 Playground 中运行,您需要一个常规应用程序来执行此操作。

  • 从技术上讲,这不是一个悬空指针,因为 Swift 会跟踪无主引用,以确保在所有强引用消失后,它总是在尝试访问它时陷入困境。您可以使用“unowned(unsafe)”来删除此安全网并获得一个实际的悬空指针,该指针会在释放后的访问中产生未定义的行为。 (4认同)
  • 将其称为“僵尸”也可能会令人困惑,因为它不会出现在 Cocoa 生态系统中与僵尸相关的任何内容中。这是 Swift 特有的东西,一个无主引用,而不是一个悬空指针。但我认为这增加了很多讨论(指出有一个特定于 Swift 的东西可以在没有 Unsafe 的情况下导致与内存相关的幽灵般的远距离崩溃),只要读者不被引导到相信这和NSZombie有关系。 (2认同)

Mar*_*n R 6

僵尸对象是已被释放但仍接收消息的Objective-C 对象。

在 Objective-C 中,__unsafe_unretained可用于创建一个指向对象的附加指针,该对象不会增加引用计数。在 Swift 中那就是unowned(unsafe).

这是一个独立的示例:

import Foundation

class MyClass: NSObject {
    func foo() { print("foo"); }

    deinit { print("deinit") }
}

unowned(unsafe) let obj2: MyClass
do {
    let obj1 = MyClass()
    obj2 = obj1
    print("exit scope")
}
obj2.foo()
Run Code Online (Sandbox Code Playgroud)

obj1是指向新创建的对象的指针,并且obj2是指向同一对象的另一个指针,但不增加引用计数器。当do { ... }块离开时,对象被释放。通过向其发送消息obj2会导致僵尸错误:

exit scope
deinit
*** -[ZombieTest.MyClass retain]: message sent to deallocated instance 0x1005748d0
Run Code Online (Sandbox Code Playgroud)

如果您使用Managed(例如,将指向 Swift 对象的指针转换为 Cvoid指针,以便将它们传递给 C 回调函数)并且没有选择正确的保留/未保留组合,也可能会发生这种情况。一个人为的例子是:

let obj2: MyClass
do {
    let obj1 = MyClass()
    obj2 = Unmanaged.passUnretained(obj1).takeRetainedValue()
    print("exit scope")
}
obj2.foo()
Run Code Online (Sandbox Code Playgroud)


归档时间:

查看次数:

4430 次

最近记录:

2 年,8 月 前