1 min read

weak or unowned?

In Swift, we often use weak or unowned to break reference cycles for closures. I use unowned a lot because I don't have to handle optinal type. I love concise code. For example:

class ViewController: UIViewController {
    lazy var api: API = {
        let api = API()
        api.completionHandler = { [unowned self] obj in
            self.doSomething()
        }
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        api.request()
    }
}

While I know it's safer to use weak, everthing seems OK. Then I got some mysterious crashes.

SIGABRT
0 libsystem_kernel.dylib  __pthread_kill + 8
1 libsystem_pthread.dylib _pthread_kill$VARIANT$mp + 376
2 libsystem_c.dylib abort + 140
3 libswiftCore.dylib  swift::warning(unsigned int, char const*, ...) + 0
4 libswiftCore.dylib  swift::swift_abortRetainUnowned(void const*) + 32
5 libswiftCore.dylib  swift_unknownUnownedTakeStrong + 0

It's not obvious what's wrong until I did a quick search and read this. So it's not safe to use unowned for an asynchronously executed closure. ViewController may be deallocated before the api response return.

I was frustrated, it seems stupid for me to let this happen when I can avoid it (luckily it was fixed in alpha release). I'll be more cautious when I use unowned next time.

Also, it made me think more broadly about the way I treat defensive programming. I always put conciseness before safety unintentionally without knowing the hidden danger it brings. While conciseness is good, it's not worth it if your code become fragile and easy to crash.