An experiment on Swift references

Nov 2, 2023

In Swift, we have 4 different kinds of references, so I did a little experiment to help me to have a better understanding of them. There are many articles already explained the difference between these different kinds and their use case respectively, in a detailed and understandable manner, therefore in this post I'll focused on what will actually happened if you deallocate them.

Setting up

First we have this piece of code

class MagicBox {
    
    var strongObj: AnyObject?
    
    weak var weakObj: AnyObject?
    
    /// this is equivalent to unowned(safe)
    /// don't make this optional in real project
    unowned var unownedObject: AnyObject?
    
    unowned(unsafe) var unownedUnsafeObject: AnyObject?
    
    init(strongObj: AnyObject, weakObj: AnyObject, unownedObj: AnyObject, unownedUnsafeObj: AnyObject) {
        self.strongObj = strongObj
        self.weakObj = weakObj
        self.unownedObject = unownedObj
        self.unownedUnsafeObject = unownedUnsafeObj
    }
    
    func all() -> String {
        // workaround so to access these variables first
        if (self.unownedUnsafeObject == nil) {}    
        if (self.unownedObject == nil) {}
        
        return "strongObj: \(strongObj)\nweakObj: \(weakObj)\nunownedObject: \(unownedObject)\nunownedUnsafeObject: \(unownedUnsafeObject)"
    }
}

Then we create some objects and referenced them in our MagicBox

var strongObj: AnyObject? = NSObject()
var weakObj: AnyObject? = NSObject()
var unownedObj: AnyObject? = NSObject()
var unownedUnsafeObj: AnyObject? = NSObject()

var magicBox = MagicBox(strongObj: strongObj!, weakObj: weakObj!, unownedObj: unownedObj!, unownedUnsafeObj: unownedUnsafeObj!)
magicBox.all()

Here is the output:

strongObj: Optional(<NSObject: 0x600002cfcb80>)
weakObj: Optional(<NSObject: 0x600002cfcba0>)
unownedObject: Optional(<NSObject: 0x600002cfcc10>)
unownedUnsafeObject: Optional(<NSObject: 0x600002cfcc20>)

Now we try to set the value of strongObj and weakObj to nil, and here is what happened, the weakObj is vanished but the strongObj is still there.

strongObj: Optional(<NSObject: 0x600002c08cd0>)
weakObj: nil
unownedObject: Optional(<NSObject: 0x600002c08d60>)
unownedUnsafeObject: Optional(<NSObject: 0x600002c08d70>)

Next we are going to set unownedUnsafeObject to nil, and we got an error

error: Execution was interrupted, reason: EXC_BAD_ACCESS (code=1, address=0x614744894bc0).
The process has been left at the point where it was interrupted, use "thread return -x" to return to the state before expression evaluation.

Finally is the unownedObject, and we got a different error

error: Execution was interrupted, reason: signal SIGABRT.
The process has been left at the point where it was interrupted, use "thread return -x" to return to the state before expression evaluation.