Core Data应用开发实践指南
上QQ阅读APP看书,第一时间看更新

2.6 单精度浮点数与双精度浮点数

对于属性来说,单精度浮点数(float)和双精度浮点数(double)这两种数据类型可以看作带小数点的非整数。它们都可以用来表示实数,但也都有一定的限制。单精度浮点数与双精度浮点数都使用以2为底的数制(也叫二进制),对CPU来说,这是一种原生的数制,它容易引起舍入误差。以1/5这个分数为例,如果采用十进制,那我们可以精确地将其写为0.2,但如果改用二进制,则只能表示出它的近似值。小数点后面的数位越多,精度就越高,表示出来的近似值也就越准确。高精度的数会占用更多内存,以保持其准确性。

与单精度浮点数相比,双精度浮点数所包含的二进制位(bit)的个数是它的两倍。单精度浮点数用32个二进制位来保存其数据,而双精度浮点数则占用64个二进制位。两者都采用科学计数法,也就是说,整个浮点数是由尾数和指数(表示2的多少次幂)组成的。双精度浮点数有64个二进制位,这意味着它的取值范围比单精度浮点数广,精度也比单精度浮点数高。

在iOS中,最大的单精度浮点数是340 282 346 638 528 859 811 704 183 484 516 925 440.000 000。单精度浮点数与双精度浮点数都有符号位(sign bit),所以,iOS中数值最小的单精度浮点数是340 282 346 638 528 859 811 704 183 484 516 925 440.000 000,而数值最大的双精度浮点数则比数值最大的单精度浮点数还要大出许多。本章最后有道习题,题中给出了一段代码,可以打印出各种数值数据类型的最小值与最大值。

在单精度浮点数和双精度浮点数之间取舍时,需要考虑正在配置的这个属性有何特点:它的最小取值和最大取值是多少?是不是真的需要超过7位的精度(单精度浮点数所提供的精度大约是7位)?如果不需要的话,那么在iOS平台上还是应该选用单精度浮点数,因为在64位的iPhone 5S出品之前,单精度浮点数这种数据类型更能够同底层的处理器相匹配。虽说使用一大批双精度浮点数特性看上去有些不太合理,但是要注意:数据库可能会导致程序在存储量方面的需求变得比想象中更大,因为它们可能要包含巨量的数据行。目前设备的能力和容量都很大,所以在大多数情况下,使用双精度浮点数其实也可以。如果追求浮点运算的速度,同时又不太关心精度,那么选用单精度浮点类型会更加合适。但在涉及“元”或“分”等货币单位的财务计算中,则不应该使用单精度浮点数或双精度浮点数,因为“舍入误差”会导致钱数出错!

根据实体来创建NSManagedObject子类时,如果实体中某个属性的类型为单精度浮点类型或双精度浮点类型,那么在创建好的子类里,相关特性的类型就会是NSNumber。

2.6.1 小数

在涉及货币或其他十进制运算的场合中,建议把属性的数据类型设为小数(decimal)。与二进制不同,对于CPU来说,十进制并不是原生的数制,这就意味着以小数来运算时,处理器会有比较大的开销。与单精度浮点数和双精度浮点数一样,小数也是由尾数(该尾数是个整数)、指数及符号组成的。虽说内存占用量和处理时间都比较多,但是小数的计算精度却很高。在这种数制里,0.1这个数就可以精确地表示出来了。如果属性的数据类型是小数,那么它就会把0.1存储为“1/10^1”。

与值最大的双精度浮点数值相比,值最大的小数其实并不算大,但它的精度却比双精度浮点数高出许多,而且在有些时候甚至是完全准确的。本章最后有道习题,题中给出的那段代码会以1/3这个数为例,打印出每一种“数值数据类型”所能达到的精度。

根据实体来创建NSManagedObject子类时,如果实体中某个属性的类型是小数类型,那么在创建好的子类里,相关特性的类型就会是NSDecimalNumber。在NSDecimal-Number上面执行计算时要注意:若想保留精度,则只能使用NSDecimalNumber内置的方法。

2.6.2 字符串

对于属性来说,字符串这种数据类型可以存放字符数组(array of character)或普通文本(plain old text)。作为Objective-C程序员,你应该已经对字符串相当熟悉了。根据实体来创建NSManagedObject子类时,如果实体中某个属性的数据类型是字符串,那么在创建好的子类里,相关特性的类型就会是NSString。

2.6.3 Boolean

对于属性来说,Boolean这种数据类型可用来存放“是”或“否”这两种值。根据实体来创建NSManagedObject子类时,如果实体中某个属性的数据类型是Boolean,那么在创建好的子类中,相关特性的类型就是NSNumber。若想从NSNumber中获取Boolean值,只需向该实例发送boolValue消息即可。而若想将NSNumber设置为某个Boolean值,则可使用numberWithBool方法。

2.6.4 日期类型

顾名思义,日期(date)这种数据类型就是用来在属性中保存日期和时间的。根据实体来创建NSManagedObject子类时,如果实体中某个属性的类型是日期类型,那么在创建好的子类中,相关特性的类型就是NSDate。

2.6.5 二进制数据类型

如果要保存照片、音频或其他由“0”、“1”二进制位所组成的连续BLOB,那么就应该把属性的类型设为二进制数据类型(Binary Data)。根据实体来创建NSManaged-Object子类时,如果某个属性的类型是二进制数据,那么在创建好的子类中,相关特性的类型就是NSData。至于如何在数据和NSData之间转换,那要依照具体存储的数据来定。二进制数据较常见的用途就是存储照片。存储照片时,可以通过UIImagePNGRepresentation()或UIImageJPEGRepresentation()来把UIImage转换成NSData。而获取照片时,则可以通过UIImage的类方法imageWith-Data把NSData转换为UIImage。二进制数据这种数据类型对于大文件来说比较合适,因为在属性的设置选项中,我们可以开启Allows External Storage,将其“无缝地”存储在数据库之外。启用了这个选项之后,Core Data就会自行判断是把文件存放在数据库内的效率高还是存放到数据库外的效率高。

2.6.6 可变类型

可变(Transformable)数据类型很适合用来把Objective-C对象存放到属性里。这种属性类型比较灵活,它可以存放任意类的实例。比方说,UIColor类的实例就可以保存在类型为可变类型的属性里。在根据实体来创建NSManagedObject子类时,如果实体中某个属性的类型是可变类型,那么在创建好的子类中,相关特性的类型就是id。若想把id对象放入存储区(或将其从存储区里取出来),则需借助NSValueTransformer类的实例或NSValueTransformer子类的实例。NSValueTransformer类可以在属性与NSData之间“透明地”执行转换。转换过程也比较简单,尤其当待存储的类本身已经实现了NSCoding协议时更是如此。假如实现了该协议,那么系统就会提供默认的transformer,而这个transformer自己知道如何“压缩”(archive)或“解压缩”(un-archive)相关的对象。

请按下列步骤修改Grocery Dude,以便配置其中的属性:

1.将name属性的Type设为String。

2.将quantity属性的Type设为Float。

3.在Item实体中添加名为photoData的属性,并将其Type设为Binary Data。这个属性用来存放货品照片的图像信息。(注意:目前并不启用Allows External Storage,本书后面再讲解何时启用它。)

4.在Item实体中添加名为listed的属性,并将其Type设为Boolean。这个属性用来表示货品是否已出现在购物清单中。

5.在Item实体中添加名为collected的属性,并将其Type设为Boolean。如果用户已经拿到了所要购买的货品,那么这个属性就是“真”,这表示该货品已经可以从购物清单中勾掉了。

执行完上述步骤之后的数据模型如图2-4所示,现在每个属性的数据类型都已经配置好了。

图2-4 在Table风格的编辑器中显示的Grocery Dude数据模型

为了给以后更加复杂的数据模型做准备,笔者在这里告诉你如何切换到图形化的编辑界面。要改变编辑器的显示模式,只需点击图2-5正下方的Editor Style按钮。图2-5左侧演示了编辑器在Graph风格下的样貌。

图2-5 在Graph风格的编辑器中显示的Grocery Dude数据模型