How to debug [NSObject(NSKeyValueObserverNotification) willChangeValueForKey:] crash

Our app is using a custom split view controller that we're getting rid of for the next major release, but for the CURRENT release we still have it. The problem I have is that on iOS 9 we're seeing the above when we hide the master view and show it a few times over and over agian.


Its random where it ends up crashing, but it always EXC_BAD_ACCESS. So…I fire up Instruments and Zombies…no issues. Does not crash. I try it with Runtime address sanitization, and finally the regular stuff. When using the any of the above - it does not crash ever. When turning it off (and of course running in Release mode), it crashes after a few tries.


#1
0x000000010c54d398 in -[NSObject(NSKeyValueObserverNotification) willChangeValueForKey:] ()
#2
0x000000010f171bb9 in CAAnimation_setter(CAAnimation*, unsigned int, _CAValueType, void const*) ()
#3
0x000000010f16b6de in -[CAPropertyAnimation setKeyPath:] ()
#4
0x000000010f16b412 in +[CAPropertyAnimation animationWithKeyPath:] ()
#5
0x000000010d0e7c04 in +[UIViewSpringAnimationState defaultSpringAnimationForKey:mass:stiffness:damping:velocity:] ()
#6
0x000000010d0e7de2 in -[UIViewSpringAnimationState _defaultAnimationForKey:] ()
#7
0x000000010d0e7a8b in -[UIViewSpringAnimationState animationForLayer:forKey:forView:] ()
#8
0x000000010ca572cc in -[UIViewAnimationState actionForLayer:forKey:forView:] ()
#9
0x000000010ca77686 in +[UIView(Animation) _defaultUIViewActionForLayer:forKey:] ()
#10
0x000000010ca853a6 in -[UIView(CALayerDelegate) actionForLayer:forKey:] ()
#11
0x000000010f18a9fc in -[CALayer actionForKey:] ()
#12
0x000000010f1861c6 in actionForKey(CALayer*, CA::Transaction*, NSString*) ()
#13
0x000000010f186154 in CA::Layer::begin_change(CA::Transaction*, unsigned int, objc_object*&) ()
#14
0x000000010f187839 in CA::Layer::set_position(CA::Vec2<double> const&, bool) ()
#15
0x000000010f187959 in -[CALayer setPosition:] ()
#16
0x000000010f187fbd in -[CALayer setFrame:] ()
#17
0x000000010cf30311 in __26-[_UILabelLayer setFrame:]_block_invoke ()
#18
0x000000010cf3016f in -[_UILabelLayer _setFrameOrBounds:settingAction:] ()
#19
0x000000010cf302b3 in -[_UILabelLayer setFrame:] ()
#20
0x000000010ca6d3f7 in -[UIView(Geometry) setFrame:] ()
#21
0x000000010cc2d7e6 in -[UILabel setFrame:] ()


I'm quite sure its my bug somewhere having to do with memory…but I'm at a loss where to move forward with this. I'd love some ideas 😟

Crashes like this typically mean that you have an invalid KVO observer on the object. One approach I’ve found useful (above and beyond the approaches you’ve already mentioned) is to look at the

observationInfo
property of the object in question; it’s declared as a
void *
but when you’re debugging it’s generally fine to treat it
id
, whereupon you’ll find that it responds to
po
nicely.

Share and Enjoy

Quinn "The Eskimo!"
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

So, the problem was that we were adding KVO on a model class that we didnt' remove when deallocated. It had nothing to do at all with the UIView/UIKit at all.


I thought iOS 9 automatically removed observers when deallocated?

I thought iOS 9 automatically removed observers when deallocated?

Ah, you almost had me there (-: The change you’re referring to relates to NSNotificationCenter, not KVO. See WWDC 2015 Session 202 What’s New in Cocoa.

AFAIK KVO still works as it always has, that is, it retains nothing and thus requires manual removal.

Share and Enjoy

Quinn "The Eskimo!"
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

I am seeing a number of very similar crashes, they all crash at the same point as well (although they get there in different ways). These are crash logs from customers, I am not able to reproduce the crash myself. I think it is unlikey that it is an "invalid KVO observer", because it once occurred during the construction of a brand new UIView, during the following line of code:


    UITextField *value = [[UITextField alloc] initWithFrame:frame];


It is probably some weird memory problem, but I am at a loss as how to debug this, as I cannot reproduce it myself. I have tried the usual approaches (zombies, Address Sanitizer, Instruments).


Any hints are welcome.


Here is the relevant part of the crash log:


Exception Type:  EXC_BAD_ACCESS (SIGSEGV)
Exception Subtype: KERN_INVALID_ADDRESS at 0x0000000000000010
Triggered by Thread:  0

Thread 0 name:  Dispatch queue: com.apple.main-thread
Thread 0 Crashed:
0   libobjc.A.dylib               0x0000000181981b90 objc_msgSend + 16
1   Foundation                     0x0000000182bf2454 -[NSObject(NSKeyValueObserverNotification) willChangeValueForKey:] + 324
2   QuartzCore                     0x0000000184deb11c CA::Layer::set_delegate(objc_object*) + 72
3   UIKit                         0x0000000187456f40 -[UIView _createLayerWithFrame:] + 580
4   UIKit                         0x0000000187456a00 UIViewCommonInitWithFrame + 688
5   UIKit                         0x00000001874566f0 -[UIView initWithFrame:] + 140
6   UIKit                         0x000000018745dfb0 -[UILabel initWithFrame:] + 48
7   UIKit                         0x0000000187542064 -[UITextField createTextLabelWithTextColor:] + 76
8   UIKit                         0x0000000187541c30 -[UITextField initWithFrame:] + 416
9   Flyskyhy                       0x00000001000e1f68 -[ElementView doInitWithUnit:] (ElementView.m:178)

I'm seeing the same thing - sometimes from a UITextView creation but sometimes with none of my own code in the callstack.

Did you ever get this pinned down?

The conclusion that it isn't an invalid KVO observer because it happens during initialization of an object is not correct. For historical reasons, KVO uses a side table to associated object addresses with observers. That means that if the original observee was deallocated, then the next object that occupies that same address is, de facto, the new observee. KVO will act as though the new object is being observed and will try to emit change notifications to possibly-also-deallocated observers.

So if you forget to remove any KVO observer at all within any class in your code, the your UI starts crashing. Shouldn't the implementation of KVO be less fragile?

So if you forget to remove any KVO observer at all within any class in your code, the your UI starts crashing.

Forgetting to remove a KVO observer is likely to cause all sorts of weird crashes. Your weapon of choice for finding problems like this is Zombies.

Shouldn't the implementation of KVO be less fragile?

Yes. And this is one of many things that KVO should do better. As always, if stuff like this is getting in the way of your development, please file a bug report / enhancement request about it.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"
How to debug [NSObject(NSKeyValueObserverNotification) willChangeValueForKey:] crash
 
 
Q