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.