【问题标题】:Store UIColor in CoreData将 UIColor 存储在 CoreData 中
【发布时间】:2015-05-25 09:18:32
【问题描述】:

如何在 64 位 上存储 UIColor 到 CoreData 而不会丢失? 在 32 位上返回正确的 UIColor。

CoreData 设置

  • 属性类型:可变形
  • NSManagedObject 子类属性:@NSManaged var color: UIColor?

颜色值存储前

color.getRed(&red, green: &green, blue: &blue, alpha: &alpha)

输出例如64 位上的红色:

0.20000000000000018  

在 32 位上输出红色

0.199999928

从CoreData检索颜色后

color.getRed(&red, green: &green, blue: &blue, alpha: &alpha)

输出例如64 位上的红色:

0.20000000298023224 

在 32 位上输出红色:

0.199999928

导致的问题

使用 == 的颜色比较在 64 位上失败,因为值略有不同。在 32 位上一切正常,颜色比较成功。

【问题讨论】:

  • 32 位和 64 位浮点数都不能准确存储数字 0.2。在大多数情况下,将浮点数与 == 进行比较是不好的。
  • 已尝试将颜色值保存为十六进制,以完全避免精度问题。
  • 你是如何归档 UIColor 的?用 float / double 做 == 绝不是一个聪明的主意!查看stackoverflow.com/questions/1275662/…

标签: swift core-data nsdata uicolor transformable


【解决方案1】:

NSKeyedArchiver.archivedDataWithRootObject(color) 分配给数据变量并将其保存到您的Core Data 存储中。

要读取数据,只需将NSKeyedUnarchiver.unarchiveObjectWithData(colorData) 分配给颜色变量。


如果您想知道如何比较浮点数,您可以随时参考this

【讨论】:

  • 我认为如果属性被定义为“Transformable”,Core Data 运行时会自动进行这些转换。 – 我不确定这是否解决了问题所涉及的 32 位和 64 位浮点问题。
  • 无论如何你都不应该直接比较浮点数。 64 位精度更高,但保存到数据存储时会四舍五入,取出时会留下不同的值。使用 epsilon :)
  • 我将 UIColor 对象与 color == colorRetrievedFromCoreData 进行比较。四舍五入的 CoreData 似乎是问题所在。正如您所建议的,这可以通过将其存储为 NSData 来规避,其中包含 UIColor 对象的编码形式。
  • 如果人们想使用上下文发送 UIColor 来观看,他们将需要这个。使 coreData 属性可转换是存储 UIColor 的另一个选项,但如果不转换为 NSData,您将无法传输它。仅供参考
【解决方案2】:

这解决了我的问题:

  1. 将 CoreData 属性名称设置为我的自定义 NSValueTransformer 子类:MQColorTransformer

  2. 实现了以下 NSValueTransformer 子类(它也将 UIColor 转换为 NSData,就像 Schemetrical 建议的那样,但它的优点是我仍然可以将 UIColor 分配给我的颜色属性并且 NSValueTransformer 会小心幕后的转变):

NSValueTransformer 子类

import Foundation
import UIKit

@objc(MQColorTransformer) class MQColorTransformer: NSValueTransformer {

  override class func transformedValueClass() -> AnyClass{
      return NSData.classForCoder()
  }

  override func transformedValue(value: AnyObject!) -> AnyObject {

      // Transform UIColor to NSData
      let color = value as! UIColor

      var red: CGFloat = 0
      var green: CGFloat = 0
      var blue: CGFloat = 0
      var alpha: CGFloat = 0
      color.getRed(&red, green: &green, blue: &blue, alpha: &alpha)

      var components:[CGFloat] = [red, green, blue, alpha]
      let dataFromColors = NSData(bytes: components,
          length: sizeofValue(components)*components.count)

      return dataFromColors

  }

  override func reverseTransformedValue(value: AnyObject!) -> AnyObject {

      // Transform NSData to UIColor
      let data = value  as! NSData
      var components = [CGFloat](count: 4, repeatedValue:0)
      var length=sizeofValue(components)

      data.getBytes(&components, length: length * components.count)
      let color = UIColor(red: components[0],
          green: components[1],
          blue: components[2],
          alpha: components[3])

      return color
  }
}

【讨论】:

  • 请注意(除非我弄错了)这种表示与设备无关。如果颜色在 32 位设备上存档并在 64 位设备上检索(反之亦然),结果将是错误的。
  • 您意识到对于第一个函数,您可以return NSKeyedArchiver.archivedDataWithRootObject(color) 并摆脱其余代码。对于第二个函数,您可以return NSKeyedUnarchiver.unarchiveObjectWithData(data) 并摆脱其余代码。它是一种更优雅的解决方案,而不是检索颜色组件。
  • @Schemetrical :这不起作用,因为在 64 位上比较再次失败。
  • 您不应该直接比较浮点数并依赖相同类型的值进行比较,使用 FLT_EPSILON :)
  • @Schemetrical: 所以你的意思是检查两种颜色之间的距离是否是例如小于 FLT_EPSILON ?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-10-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多