CGFloat 和 NSNumber 之间的转换,无需不必要地提升为 Double
Conversion between CGFloat and NSNumber without unnecessary promotion to Double
众所周知,CGFloat
(在 CoreGraphics、UIKit 等中无处不在)
可以是 32 位或 64 位浮点数,具体取决于
处理器架构。
在 C 中,CGFloat
它是一个类型别名
到 float
或 double
,在 Swift 中它被定义为一个 struct CGFloat
与
a native
属性(即 Float
或 Double
)。
反复观察到 NSNumber
可以从
并转换为 Float
和 Double
,但不存在
与 CGFloat
之间的类似转换。一般建议
(例如 )是为了
通过 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?这看起来不太适合未来。
也可以使用 CGFLOAT_IS_DOUBLE
常量(例如
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
的精度并适用于 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 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" 初始化方法具有相同的外部
参数名称 doubleOrFloat
但参数类型不同。从实际
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 初始化程序。
众所周知,CGFloat
(在 CoreGraphics、UIKit 等中无处不在)
可以是 32 位或 64 位浮点数,具体取决于
处理器架构。
在 C 中,CGFloat
它是一个类型别名
到 float
或 double
,在 Swift 中它被定义为一个 struct CGFloat
与
a native
属性(即 Float
或 Double
)。
反复观察到 NSNumber
可以从
并转换为 Float
和 Double
,但不存在
与 CGFloat
之间的类似转换。一般建议
(例如 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?这看起来不太适合未来。
也可以使用 CGFLOAT_IS_DOUBLE
常量(例如
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
的精度并适用于 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 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" 初始化方法具有相同的外部
参数名称 doubleOrFloat
但参数类型不同。从实际
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 初始化程序。