众所周知,CGFloat
(在CoreGraphics,UIKit等中无处不在)可以是32位或64位浮点数,具体取决于处理器架构.
在C中,CGFloat
它是一个typealias到float
或double
,在Swift则它被定义为struct CGFloat
具有native
属性(其是Float
或Double
).
已经多次观察到NSNumber
可以从被创建并转换为Float
和Double
,但有从与存在不相似的转换CGFloat
.一般建议(例如在Swift中将CGFloat转换为NSNumber)是通过转换Double
CGFloat <--> Double <--> NSNumber
例:
let c1 = CGFloat(12.3) let num = NSNumber(double: Double(c1)) let c2 = CGFloat(num.doubleValue)
这很简单,也没有精确度.此外,大多数平台现在都是64位,然后CGFloat/Double
转换很简单,可能由编译器优化.
然而,这引起了我的好奇心,如果转换可以做到
无需促进CGFloat
到Double
32位平台.
可以使用构建配置语句(例如,应该使用条件编译来处理不同体系结构中CGFloat的差异吗?):
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 } }
但是如果将Swift移植到其他不是英特尔或ARM的架构上怎么办?这看起来不是很好的证据.
也可以使用CGFLOAT_IS_DOUBLE
常量(例如,
来自CGFloat的NSNumber):
if CGFLOAT_IS_DOUBLE != 0 { // ... } else { // ... }
这里的缺点是编译器将始终在其中一个案例上发出 "永不执行"警告.
所以长话短说:
我们如何转换之间
CGFloat
,并NSNumber
以安全的方式,没有编译器警告,并避免不必要的推广Double
?
请注意,这是一个"学术"问题.如上所述(以及其他Q&A),人们可以简单地通过Double
实际转换.
我在这里发表了一个"自我回答"的精神, 分享你的知识,Q&A风格.当然其他答案也欢迎!
更新:可以将CGFloat
值转换为NSNumber
和返回:
let c1 = CGFloat(12.3) let num = c1 as NSNumber let c2 = num as CGFloat
这保留了CGFloat
Swift 2和Swift 3 的精度并与之一起使用.
(以前的答案 - 太复杂了):我找到了两种解决方案.第一个使用NSNumber
和之间的免费桥接CFNumber
(如最常见和正确的做法是从NSNumber获得CGFloat?
for Objective-C).它使用CFNumber
具有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()) } }
有两个私有的"helper"init方法具有相同的外部参数名称doubleOrFloat
但参数类型不同.从cgFloat.native
编译器的实际类型确定要调用哪一个
convenience init(cgFloat : CGFloat) { self.init(doubleOrFloat: cgFloat.native) }
访问器方法中的相同想法.从self.native
编译器的类型确定doubleOrFloatValue()
要调用的两个方法中的哪一个
var cgFloatValue : CGFloat { return CGFloat(floatLiteral: doubleOrFloatValue()) }