CGFloat和NSNumber之间的转换没有不必要的升级到Double

问题描述 投票:5回答:2

众所周知,CGFloat(在CoreGraphics,UIKit等中无处不在)可以是32位或64位浮点数,具体取决于处理器架构。

在C,CGFloat它是floatdouble的类型,在Swift中它被定义为具有struct CGFloat属性(nativeFloat)的Double

已经反复观察到NSNumber可以从FloatDouble创建并转换为CGFloat,但是Convert CGFloat to NSNumber in Swift没有类似的转换。一般建议(例如在Double)是通过CGFloat <--> Double <--> NSNumber 转换

let c1 = CGFloat(12.3)
let num = NSNumber(double: Double(c1))
let c2 = CGFloat(num.doubleValue)

例:

CGFloat/Double

这很简单,也没有精确度。现在大多数平台都是64位,然后CGFloat转换是微不足道的,可能由编译器优化。

但是,如果在没有在32位平台上将Double推广到Should conditional compilation be used to cope with difference in CGFloat on different architectures?的情况下进行转换,就会引起我的好奇心。

可以使用构建配置语句(例如在extension NSNumber { convenience init(cgFloatValue value : CGFloat) { #if arch(x86_64) || arch(arm64) self.init(double: value.native) #else self.init(float: value.native) #endif } } 中):

CGFLOAT_IS_DOUBLE

但是如果将Swift移植到其他不是英特尔或ARM的架构上怎么办?这看起来不是很好的证据。

也可以使用NSNumber from CGFloat常数(例如在 if CGFLOAT_IS_DOUBLE != 0 { // ... } else { // ... } 中):

CGFloat

这里的缺点是编译器将始终在其中一个案例上发出“永不执行”警告。

所以长话短说:

  • 如何在没有编译器警告的情况下以安全的方式在NSNumberDouble之间进行转换,并且没有不必要的促销到Double

请注意,这是一个“学术”问题。如上所述(以及其他问答),人们可以简单地通过share your knowledge, Q&A-style实际转换。

我在CGFloat的精神中发布了一个“自我回答”。当然其他答案也欢迎!

swift nsnumber cgfloat
2个回答
13
投票

更新:可以将NSNumber值转换为let c1 = CGFloat(12.3) let num = c1 as NSNumber let c2 = num as CGFloat 并返回:

CGFloat

这保留了NSNumber的精度,并与Swift 2和Swift 3一起使用。


(以前的答案 - 太复杂了):我找到了两种解决方案。第一个使用CFNumberWhat is most common and correct practice to get a CGFloat from an NSNumber?之间的免费桥接(如CFNumber for Objective-C)。它使用CGFloatextension NSNumber { // CGFloat -> NSNumber class func numberWithCGFloat(var value: CGFloat) -> NSNumber { return CFNumberCreate(nil , .CGFloatType, &value) } // NSNumber -> CGFloat var cgFloatValue : CGFloat { var value : CGFloat = 0 CFNumberGetValue(self, .CGFloatType, &value) return value } } 值具有专用转换模式的事实:

init

这很简单。唯一的缺点:我无法弄清楚如何使构造函数成为class method方法而不是extension NSNumber { // CGFloat -> NSNumber private convenience init(doubleOrFloat d : Double) { self.init(double : d) } private convenience init(doubleOrFloat f : Float) { self.init(float : f) } convenience init(cgFloat : CGFloat) { self.init(doubleOrFloat: cgFloat.native) } // NSNumber -> CGFloat private func doubleOrFloatValue() -> Double { return self.doubleValue } private func doubleOrFloatValue() -> Float { return self.floatValue } var cgFloatValue : CGFloat { return CGFloat(floatLiteral: doubleOrFloatValue()) } }

第二种可能的解决方案有点长:

doubleOrFloat

有两个私有的“helper”init方法,它们具有相同的外部参数名称cgFloat.native但参数类型不同。根据 convenience init(cgFloat : CGFloat) { self.init(doubleOrFloat: cgFloat.native) } 的实际类型,编译器确定要调用哪一个

self.native

访问器方法中的相同想法。根据doubleOrFloatValue()的类型,编译器确定调用两个 var cgFloatValue : CGFloat { return CGFloat(floatLiteral: doubleOrFloatValue()) } 方法中的哪一个

let num = NSNumber(double: 1.0)
let c: CGFloat? = CGFloat(exactly: num)

0
投票
documentation

这是初始化器的qazxswpoi。

© www.soinside.com 2019 - 2024. All rights reserved.