Just about all iOS tweaks make use of private headers and in some way affect the UI. When I released OS Experience, there was a bug that users were experiencing, where SpringBoard would crash when multitasking gestures were used. Somehow this bug managed to get past beta and into the final product. How, I’m still not entirely sure, but that’s beside the point.
I narrowed the bug down to only 32-bit devices, of which I do not own any on iOS 7. I had to remotely debug a beta tester’s device, and after a few hours of painful searching, traced the bug down to this line:
- (void)handleFluidScaleSystemGesture:(SBScaleGestureRecognizer*)arg1{
float percentage = [arg1 cumulativeMotion] / [arg1 animationDistance];
...
}
The problem was that percentage became NaN on 32-bit devices, and when it is used later on as a coordinate for a UIView, it crashes. My question was, why did this always happen? Well…[arg1 animationDistance] was always zero. I divided by it.
This was troublesome for me, as that value is essential to the operation of OS Experience. I initially assumed that SpringBoard did not implement that value correctly on 32-bit devices, and began reverse engineering its binary, to no avail.
Take a look at SBScaleGestureRecognizer’s header.
@interface SBFluidSlideGestureRecognizer : SBGestureRecognizer @property(nonatomic) double animationDistance; ... @end
Note the type for animationDistance. It is a double. In some headers I found online, the type is a float. As it turns out, the type is actually a CGFloat, which is a double on 64-bit devices and a float on 32-bit devices. (Thanks to @limneos for this.)
Now, I know what you’re thinking… “Can’t the float just be casted to a double?“. No. No it can’t. You have forsaken Objective-C by not learning the runtime, and as a result, it refuses to reply to your messages. It ignores you.
Why? Because of this function: double objc_msgSend_fpret(id self, SEL op, ...). It is used to retrieve floats as a return value for messages, due to incompatibilities with the 32-bit ARM ABI. Since the header is from a 64-bit device and specifies only a double, the compiler doesn’t know to use it, proceeds to use the normal objc_msgSend, and the result is zero.
My point is, class-dump could be upgraded to support fat binaries, by dumping the class headers for both 32 and 64 bit slices and then comparing them. Preprocessor macros could then be used for each difference, like so:
@interface SBFluidSlideGestureRecognizer : SBGestureRecognizer #ifdef __LP64__ @property(readonly, nonatomic) double activeRecognitionDuration; #else @property(readonly, nonatomic) float activeRecognitionDuration; #endif @end
I have looked into implementing this myself, but class-dump has a very large code base, and I believe it would take quite a large change in the overall structure to support this. I just don’t have the time to implement it myself at the moment, but it’s on my list. For now; just watch for floats in private headers.
