mfaani
mfaani

Reputation: 36427

How can I demonstrate a zombie object in Swift?

I've read How to demonstrate memory leak and zombie objects in Xcode Instruments? but that's for objective-c. The steps don't apply.

From reading here I've understood zombies are objects which are:

not exactly sure how that's different from accessing a deallocated object.

I mean in Swift you can do:

var person : Person? = Person(name: "John")
person = nil
print(person!.name)

Is person deallocated? Yes!

Are we trying to point to it? Yes!

So can someone share the most common mistake which leads to creating a dangling pointer?

Upvotes: 6

Views: 5434

Answers (4)

yoAlex5
yoAlex5

Reputation: 34391

Swift Zombie Object

Zombie Object is a deallocated object(ref count is nil) which behaves itself like alive(receive messages). It is possible because of dangling pointer.

dangling pointer points on some address in the memory where data is unpredictable

  • Objective-C has unsafe_unretained[About] property attributes. [Example]

  • Swift has

    • unowned(unsafe)[About] reference
    • Unmanaged - a wrapper for non ARC Objc- code
unowned(unsafe) let unsafeA: A

func main() {
    let a = A() // A(ref count = 1)
    unsafeA = a 
} // A(ref count = 0), deinit() is called

func additional() {
    unsafeA.foo() //<- unexpected
}

Upvotes: 1

Martin R
Martin R

Reputation: 540005

Zombie objects are Objective-C objects which have been deallocated, but still receive messages.

In Objective-C, __unsafe_unretained can be used to create an additional pointer to an object which does not increase the reference count. In Swift that would be unowned(unsafe).

Here is a self-contained example:

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

obj1 is a pointer to a newly created object, and obj2 is another pointer to the same object, but without increasing the reference counter. When the do { ... } block is left, the object is deallocated. Sending a message to it via obj2 causes the Zombie error:

exit scope
deinit
*** -[ZombieTest.MyClass retain]: message sent to deallocated instance 0x1005748d0

This can also happen if you use Managed (e.g. to convert pointers to Swift objects to C void pointers in order to pass them to C callback functions) and you don't choose the correct retained/unretained combination. A contrived example is:

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

Upvotes: 7

Cristik
Cristik

Reputation: 32879

Here's zombie attack in under 15 lines of code:

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

What happens in this code is that Child holds an unowned reference to Parent, which becomes invalid once Parent gets deallocated. The reference holds a pointer to the no longer living parent (RIP), and accessing that causes a crash with a message similar to this:

Fatal error: Attempted to read an unowned reference but object 0x1018362d0 was already deallocated2018-10-29 20:18:39.423114+0200 MyApp[35825:611433] Fatal error: Attempted to read an unowned reference but object 0x1018362d0 was already deallocated

Note The code won't work in a Playground, you need a regular app for this.

Upvotes: 7

Rob Napier
Rob Napier

Reputation: 299565

This is not a dangling pointer or a zombie. When you use ! you're saying "if this is nil, then crash." You should not think of person as a pointer in Swift. It's a value. That value may be .some(T) or it may be .none (also called nil). Neither of those is dangling. They're just two different explicit values. Swift's nil is nothing like null pointers in other languages. It only crashes like null pointers when you explicitly ask it to.

To create zombies, you'll need to be using something like Unmanaged. This is extremely uncommon in Swift.

Upvotes: 6

Related Questions