Crash in [UIAXDelegateClearer dealloc]

Hi all,

I'm getting a nasty crash showing up in my crash-logs, but I can't for the life of me pin it down.

It's happening on various 64bit iPads with iOS9 with the following callstack, and nothing notable happening on any other threads. The code in question has been running ok since iOS6 on a variety of devices.


Users don't seem to have any accessibility features on despite the presence of a UIAX function in the callstack. Something to do with iPad gestures maybe?


Anyone got any insight into this? It's really driving me (and the affected users) nuts...


EXC_BAD_ACCESS KERN_INVALID_ADDRESS at 0x000000001468e410
Thread : Crashed: com.apple.main-thread
0  libobjc.A.dylib                0x0000000198769bd0 objc_msgSend + 16
1  UIAccessibility                0x0000000194d60f58 -[UIAXDelegateClearer dealloc] + 104
2  libobjc.A.dylib                0x00000001987676d8 _object_remove_assocations + 356
3  libobjc.A.dylib                0x000000019876208c objc_destructInstance + 104
4  libobjc.A.dylib                0x00000001987620e0 object_dispose + 28
5  UIKit                          0x000000018977350c -[UIResponder dealloc] + 140
6  UIKit                          0x00000001893d38d4 -[UIView dealloc] + 1436
7  GLKit                          0x0000000184f5c920 -[GLKView dealloc] + 228
8  Chunky Comic Reader            0x000000010013209c -[ReadView dealloc] (ReadView.mm:300)
9  libobjc.A.dylib                0x0000000198771ae8 (anonymous namespace)::AutoreleasePoolPage::pop(void*) + 508
10 CoreFoundation                 0x0000000183d64b6c _CFAutoreleasePoolPop + 28
11 CoreFoundation                 0x0000000183e360c8 __CFRunLoopRun + 1636
12 CoreFoundation                 0x0000000183d64dc0 CFRunLoopRunSpecific + 384
13 GraphicsServices               0x000000018ece0088 GSEventRunModal + 180
14 UIKit                          0x000000018943ef60 UIApplicationMain + 204
15 Chunky Comic Reader            0x00000001000fddc8 main (main.m:16)
16 libdyld.dylib                  0x0000000198f768b8 start + 4

Accepted Reply

In case anyone else runs into this, it looks like a UIKit bug.


Basically it'll happen if

- Object X has a reference to a UIScrollview SV.

- X is SV's delegate

- When X deallocs, it has the last live reference to SV

Plus it only happens on particular devices, in particular setups - I can only get it to happen in the simulator with the accessibility inspector turned on, although I've had plenty of crash reports from real devices.


Basically, UIScrollView will attach an associated 'UIAXDelegateClearer' object to its delegate, so that when the delegate is freed the associated object is too, and it's destructor can zero out the scrollview's delegate (since it's dead now).


Normally when the scrollview deallocs, it removes this UIAXDelegateClearer from it's delegate. But if the delegate is in the process of deallocing when the SV deallocs, for some reason it doesn't remove it, and it's now left pointing at a dead scrollview. Then the UIAXDelegateClearer gets freed, tries to zero the delegate on the dead scrollview, and my app crashes and makes my users sad.


edit: rdar://22812548


edit2: Oooooh, it's because they made the delegate weak in iOS9. At the start of [ReadView dealloc] it zeros out weak references to itself, which include the scrollview's delegate property. But this bypasses the scrollview's mechanism for managing the UIAXDelegateClearer because that uses setDelegate, which is not called.

Replies

I got the same question...

I check my code and find that there is an object dealloc twice in my dealloc function, not sure that is the reason.

In case anyone else runs into this, it looks like a UIKit bug.


Basically it'll happen if

- Object X has a reference to a UIScrollview SV.

- X is SV's delegate

- When X deallocs, it has the last live reference to SV

Plus it only happens on particular devices, in particular setups - I can only get it to happen in the simulator with the accessibility inspector turned on, although I've had plenty of crash reports from real devices.


Basically, UIScrollView will attach an associated 'UIAXDelegateClearer' object to its delegate, so that when the delegate is freed the associated object is too, and it's destructor can zero out the scrollview's delegate (since it's dead now).


Normally when the scrollview deallocs, it removes this UIAXDelegateClearer from it's delegate. But if the delegate is in the process of deallocing when the SV deallocs, for some reason it doesn't remove it, and it's now left pointing at a dead scrollview. Then the UIAXDelegateClearer gets freed, tries to zero the delegate on the dead scrollview, and my app crashes and makes my users sad.


edit: rdar://22812548


edit2: Oooooh, it's because they made the delegate weak in iOS9. At the start of [ReadView dealloc] it zeros out weak references to itself, which include the scrollview's delegate property. But this bypasses the scrollview's mechanism for managing the UIAXDelegateClearer because that uses setDelegate, which is not called.

thanks for your analyse, I want to know what's your solution to this problem..

If this is the bug you're hitting, the solution is to make sure the UIScrollView isn't freed inside it's delegate's dealloc.


I do something like this:


@implementation ReadView
- (void)dealloc
{
   UIScrollView *sv = self.scrollview;
   dispatch_async( dispatch_get_main_queue(), ^{ sv.tag = 123; } );
}


This keeps the scrollview alive until a little later, when it's safe to free.

Thanks!

Thanks for the solution. We also started experiencing these crashes on iOS 9 iPads when voice over was enabled. It didn't always crash with voice over - but it did often enough to be a huge pain for users.