最近遇到了一个问题,将一个NSNumber
对象存入NSUserDefaults
,取出来的数值和存入前的数值竟然不一致。
比如存入的数值为6350939786827530754
,但取出的数值变成了6350939786827531264
。
这个数并没有超过long long
的范围,从数值变化的形势(只有末尾几位发生了变化),可以判断这个数值发生了精度溢出。
但为什么将一个NSNumber
存入NSUserDefaults
再取出来之后,会发生精度溢出呢?(恐慌脸)查看了一下,存入NSUserDetaults
的那个NSNumber
对象,实际是一个NSDecimalNumber
对象,即NSNumber
的一个子类。在调用系统的JSON解析库时,数值较大的数会被解析为一个NSDecimalNumber
对象。
和NSNumber
不同的是,NSDecimalNumber
的objCType
属性,返回的永远是d
,即double,官方文档中有这样的说明:
For a decimal number object, this property always contains “d” (for double).
而在存入NSUserDefaults
中时,NSDecimalNumber
的objCType
方法会被调用,NSUserDefaults
应该会通过判断objCType
来决定如何将数据持久化到硬盘。由于NSDecimalNumber
的objCType
永远为d,所以在持久化时,它将以double的形式被持久化,导致了精度溢出。
不仅仅是存入NSUserDefaults
中,在存入sqlite和存入Core Data时,NSDecimalNumber
都有可能遇到精度溢出的问题(真坑)。
解决方法可能有这么几种:
1.在持久化之前,将NSDecimalNumber
对象转化为NSNumber
对象;
2.在持久化之前,将NSDecimalNumber
归档为NSData
,取出后unarchive回去
3.更简单粗暴的方法:持久化之前将NSDecimalNumber
转化成NSString
,取出时将NSString
再转回数值