i’ve seen evidence that UIViewController has logic to prevent its dealloc/deinit methods from running on a background thread. what seems to occur is that, if the last strong reference to a UIVC is zeroed off the main thread, then the VC is logically marked as ‘deallocating’, but actual invocation of dealloc/deinit is enqueued to the main queue. during the window of time between the beginning and end of this asynchronous deallocation, some strange issues can occur.
in particular, if the deallocating VC is a parent view controller, then its children can still access it via their parent
property. despite this property being marked as weak, a non-nil parent VC reference will be returned. if a weak reference is attempted to be stored to the parent, you get a runtime crash immediately to the effect of:
Cannot form weak reference to instance (0x1234) of class <some-UIVC-sublass>...
surprisingly, if you load the reference via the objc runtime's objc_loadWeak
method, you'll get nil
, but no crash. unsurprisingly, if a strong reference to the deallocating parent is stored and persists past its dealloc
invocation, you’ll generally end up with a segmentation violation if the reference is accessed.
i imagine the UIVC source is quite complex and there are probably good reasons to try and ensure deallocation only ever occurs on the main thread, but it seems surprising that simply passing view controller variables across threads could lead to exposing unsafe references like this. is this behavior expected? assuming not, i've preemptively filed feedback FB13478946
regarding this issue. attached is some sample code that can reliably reproduce the unexpected behavior.