swift - CGFloat 和 NSNumber 之间的转换,无需不必要地提升为 Double

标签 swift nsnumber cgfloat

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

在 C 中,CGFloat 它是一个类型别名 到 floatdouble,在 Swift 中它被定义为一个 struct CGFloatnative 属性(即 FloatDouble)。

已经反复观察到 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)

这很简单而且正确,不会丢失任何精度。 现在大多数平台都是 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 {
        // ...
    }

这里的缺点是编译器总是发出一个 “永远不会被执行” 警告其中一个案例。

长话短说:

  • 我们如何在两者之间进行转换 CGFloatNSNumber 以安全的方式,没有编译器警告, 并且没有不必要的提升到 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。


(上一个答案 - 太复杂了):我找到了两个解决方案。第一个使用免费桥接 在 NSNumberCFNumber 之间(如 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/

相关文章:

ios - 反转负数NSNumber

c - 往返文本和返回

Cocoa CGColorRef 不兼容的指针类型传递 float [4 ]' to parameter of type ' const CGFloat *' (aka ' const double *')

ios - Swift 中的 "' CGFloat ' is not convertible to ' Double '"错误(iOS)

iphone - NSNumber 超出范围?

Swift 在 0 和 1 之间随机 float

swift - 从 SceneKit 中删除所有几何体,或动态修改它

swift - UIView.animate 不是动画

ios - swift2.3 中的推送 View Controller 中导航栏颜色变为黑色

ios - 如何更改 Xcode Swift Playgrounds(.swiftpm) 项目中的字体?