众所周知,CGFloat
(在 CoreGraphics、UIKit 等中无处不在)
可以是 32 位或 64 位 float ,具体取决于
处理器架构。
在 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)
这很简单而且正确,不会丢失任何精度。
现在大多数平台都是 64 位的,然后是 CGFloat/Double
转换很简单,可能由编译器优化。
但是,是否可以进行转换,却引起了我的好奇
没有在 32 位平台上将 CGFloat
提升为 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
}
}
但是如果 Swift 被移植到其他不是的架构上呢? 英特尔还是ARM?这看起来不太适合 future 。
也可以使用 CGFLOAT_IS_DOUBLE
常量(例如在
NSNumber from CGFloat ):
if CGFLOAT_IS_DOUBLE != 0 {
// ...
} else {
// ...
}
这里的缺点是编译器总是发出一个 “永远不会被执行” 警告其中一个案例。
长话短说:
- 我们如何在两者之间进行转换
CGFloat
和NSNumber
以安全的方式,没有编译器警告, 并且没有不必要的提升到Double
?
请注意,这是一个“学术”问题。如前所述
上面(以及其他问答中)可以简单地通过 Double
进行转换
实际上。
我在这里张贴一个“ self 回答”的精神 share your knowledge, Q&A-style .当然也欢迎其他答案!
最佳答案
更新:可以将 CGFloat
值转换为 NSNumber
并返回:
let c1 = CGFloat(12.3)
let num = c1 as NSNumber
let c2 = num as CGFloat
这保留了 CGFloat
的精度并与 Swift 2 一起工作
和 swift 3。
(上一个答案 - 太复杂了):我找到了两个解决方案。第一个使用免费桥接
在 NSNumber
和 CFNumber
之间(如 What is most common and correct practice to get a CGFloat from an NSNumber?
对于 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 方法
。
第二种可能的解决方案有点长:
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())
}
}
有两个具有相同外部的私有(private)“助手”初始化方法
参数名称 doubleOrFloat
但参数类型不同。从实际
cgFloat.native
的类型由编译器决定调用哪一个
在
convenience init(cgFloat : CGFloat) {
self.init(doubleOrFloat: cgFloat.native)
}
访问器方法中的想法相同。来自self.native
的类型
编译器确定两个 doubleOrFloatValue()
中的哪一个
调用方法
var cgFloatValue : CGFloat {
return CGFloat(floatLiteral: doubleOrFloatValue())
}
关于swift - CGFloat 和 NSNumber 之间的转换,无需不必要地提升为 Double,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34301579/