众所周知,CGFloat
(在CoreGraphics,UIKit等中无处不在)可以是32位或64位浮点数,具体取决于处理器架构。
在C,CGFloat
它是float
或double
的类型,在Swift中它被定义为具有struct CGFloat
属性(native
或Float
)的Double
。
已经反复观察到NSNumber
可以从Float
和Double
创建并转换为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
这里的缺点是编译器将始终在其中一个案例上发出“永不执行”警告。
所以长话短说:
NSNumber
和Double
之间进行转换,并且没有不必要的促销到Double
?请注意,这是一个“学术”问题。如上所述(以及其他问答),人们可以简单地通过share your knowledge, Q&A-style实际转换。
我在CGFloat
的精神中发布了一个“自我回答”。当然其他答案也欢迎!
更新:可以将NSNumber
值转换为let c1 = CGFloat(12.3)
let num = c1 as NSNumber
let c2 = num as CGFloat
并返回:
CGFloat
这保留了NSNumber
的精度,并与Swift 2和Swift 3一起使用。
(以前的答案 - 太复杂了):我找到了两种解决方案。第一个使用CFNumber
和What is most common and correct practice to get a CGFloat from an NSNumber?之间的免费桥接(如CFNumber
for Objective-C)。它使用CGFloat
为extension 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)
documentation
这是初始化器的qazxswpoi。