An experiment on Swift references

Nov 3, 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.
Great! You've successfully subscribed.
Great! Next, complete checkout for full access.
Welcome back! You've successfully signed in.
Success! Your account is fully activated, you now have access to all content.