NSPort delegate
May 12, 2014
It all started with an intermittent crash. NSZombieEnabled
has helped to obtain “message sent to deallocated object” crash report with nice stack trace. And there was -[PortController handlePortMessage:]
method in stack trace. I’ve decided it would be an easy fix — just stop being NSPort
delegate in -[PortController dealloc]
. But the fix wasn’t as straightforward as I’ve expected — there was already port.delegate = nil
in -[PortController dealloc]
. So why message was still sent to deallocated delegate?
After some debugging I’ve discovered that PortController
was deallocated not before -handlePortMessage:
, but during -handlePortMessage:
. When PortController
’s owner released controller, PortController
was kept alive only by a secondary thread. PortController
sent message through NSPort
on the secondary thread, -[PortController handlePortMessage:]
was executed on the main thread and during its execution the secondary thread has finished, released and deallocated PortController
. And after that deallocated PortController
was accessed on main thread somewhere in -handlePortMessage:
.
I’ve fixed the crash by introducing -[PortController invalidate]
, in which PortController
stops being a delegate. And PortController
’s owner calls -invalidate
before it releases PortController
. Fortunately, it is performed on the main thread, so -handlePortMessage:
cannot be called during -[PortController invalidate]
. And after -invalidate
PortController
isn’t a delegate anymore and doesn’t receive -handlePortMessage:
.