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.
Member discussion