Just another iPhone Blog
longLongValue issue in NSDecimalNumber
Problem: A legacy module in current project involved JSON parsing where SBJSONParser was being used. While doing performance analysis, we found out that SBJSONParser was contributing to 50% to the load time of the module. The benchmarks done here ( https://github.com/bontoJR/iOS-JSON-Performance ) convinced us to use NSJSONSerialization, a native JSON parse in iOS 5.0 and above. Things went smooth except with the change except for the below issue
We were using 64bit number to represent primary key. NSJSonSerialization used NSDecimalNumber for representing numbers, while SBJSONParser used NSNumber to represent numbers.
With NSDecimalNumber, a number returned from its description or stringValue method found to be different from the value returned by longLongValue method implementation by NSDecimalNumber.
For eg: 3924849704573025603 = 3924849704573025792
The reason is well explained here http://stackoverflow.com/a/12421451/623569
“The problem is that an exact value of an NSDecimalValue is only representable as … anNSDecimalValue.
You can get an approximate 64 bits IEE754 value with method doubleValue. When you try to use longLongValue you effectively get the result of casting to a long long int the approximate IEE754 value.“
Many of the modules in our project were using longLongValue, means that we had no choice to do a Find and Replace in the source as application using this module was out in market used by many clients and the value was stored in the database.
How do I make sure the longLongValue of NSDecimalNumber be consistent with its stringValue method?
Categories in our Objective-C comes to our rescue.
By creating a Category of NSDecimalNumber and re-implementing longLongValue. this new implementation will convert the number to string using stringValue and return number of type long long using longLongValue method. Since, NSString uses NSNumber to make the conversion, the final value returned by longLongValue is equal to value obtained through stringValue method in NSDecimalNumber.
@implementation NSDecimalNumber (unsignedLongLongBug)
– (long long)longLongValue
return [[selfstringValue] longLongValue];
A point to note here is that we are not interested in the correctness of the value in our case, rather we are trying to make the results consistent as I am only looking for the number to be unique and not using the result for any calculation.
A word of CAUTION: Using a method in category to replace Primary class’s implementation may have unknown impact in case if there is a second category which implements the same method. In real word it depends on order in which the category is loaded and always the the method is replaced by the last category to be loaded.