当前位置:  开发笔记 > IOS > 正文

CGFloat和NSNumber之间的转换没有不必要的升级到Double

如何解决《CGFloat和NSNumber之间的转换没有不必要的升级到Double》经验,为你挑选了1个好方法。

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

在C中,CGFloat它是一个typealias到floatdouble,在Swift则它被定义为struct CGFloat具有native属性(其是FloatDouble).

已经多次观察到NSNumber可以从被创建并转换为FloatDouble,但有从与存在不相似的转换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 转换很简单,可能由编译器优化.

然而,这引起了我的好奇心,如果转换可以做到 无需促进CGFloatDouble32位平台.

可以使用构建配置语句(例如,应该使用条件编译来处理不同体系结构中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风格.当然其他答案也欢迎!



1> Martin R..:

更新:可以将CGFloat值转换为NSNumber和返回:

let c1 = CGFloat(12.3)
let num = c1 as NSNumber
let c2 = num as CGFloat

这保留了CGFloatSwift 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())
    }


应该注意的是,截至2017年4月,随着Swift 3.1的发布,NSNumber和CGFloat之间的强制转换可能会在运行时失败,并且将成为Swift 4中的一个经过检查的强制转换,因此更复杂的解决方案可能是最佳选择.
推荐阅读
mobiledu2402852357
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有