【问题标题】:Should conditional compilation be used to cope with difference in CGFloat on different architectures?是否应该使用条件编译来应对不同架构上 CGFloat 的差异?
【发布时间】:2014-06-12 12:46:48
【问题描述】:

在回答 this earlier question 关于在 CGFloat 上使用 ceil() 以针对所有架构进行编译时,我提出了一个解决方案:

    var x = CGFloat(0.5)

    var result: CGFloat

    #if arch(x86_64) || arch(arm64)
        result = ceil(x)
    #else
        result = ceilf(x)
    #endif

    // use result

(对于那些已经感到困惑的人的背景信息:CGFloat 是 32 位架构的“浮点”类型,64 位架构(即编译目标)的“双”类型,这就是为什么只使用 ceil()ceilf() 它不会总是编译,具体取决于目标架构。请注意,您似乎无法使用 CGFLOAT_IS_DOUBLE 进行条件编译,只有架构标志...)

现在,这在 cmets 中引起了一些关于在编译时与运行时修复问题的争论,等等。我认为我的回答被接受得太快了,以至于无法引起关于这个问题的一些好的辩论。

所以,我的新问题是:如果您希望您的 iOS 和 OS X 代码在 32 位和 64 位设备上运行,上述操作是否安全且明智?如果它理智和明智的,还有更好的(至少同样有效,而不是“恶心”)的解决方案吗?

【问题讨论】:

  • 真正的解决方案是提交错误报告。这种事情不应该是必要的。 Swift 需要更宽容、更智能的数字。
  • @matt 好吧,公平地说,CGFloat 实际上不是 Swift 类型,它是 Core Graphics 类型。 Swift 的 Float 始终是 32 位的,Swift 的 Double 始终是 64 位的。并且有充分的理由(性能、内存使用、精度)希望在一种语言中同时使用两者。
  • 他们知道在这方面与 Core Graphics 类型的交互会很常见。语言需要使这种交互变得容易。它应该设计用于 Cocoa。
  • @matt 好吧,这只是一个测试版。你养过雷达吗?我已经看到这个问题出现了很多,所以我相信你是对的,应该有一种更简单的方法来处理它。我问这个问题的部分原因是我想知道我是否错过了一种更简单的方法......
  • 只需将这些检查包装在 CGFloatCeil() 中

标签: swift


【解决方案1】:

马特,

以您的解决方案为基础,如果您在多个地方使用它,那么一点点扩展可能会使它更受欢迎:

extension CGFloat {
    var ceil: CGFloat {
        #if arch(x86_64) || arch(arm64)
            return ceil(x)
        #else
            return ceilf(x)
        #endif
    }
}

剩下的代码会更简洁:

var x = CGFloat(0.5)
x.ceil

【讨论】:

  • 这似乎是一个很好的干净封装。我认为扩展是有意义的。
【解决方案2】:
    var f : CGFloat = 0.5
    var result : CGFloat
    result = CGFloat(ceil(Double(f)))

告诉我我缺少什么,但这对我来说似乎很简单。

【讨论】:

  • 嗯,它比我的版本慢,大概是因为初始化了额外的变量,但这是否足以保证条件编译可能取决于你在做什么以及你做的频率.
  • @MattGibson 您是否将其作为基准进行了较慢的测试?我希望 LLVM 能够优化它。
  • @RogerNolan 我不记得了。请记住,无论如何,这将是来自非常早期版本的 Swift 编译器的评论,即使我在去年 6 月进行了基准测试,现在也不太可能是真的。跨度>
  • :-) 在出现真正的性能问题之前,我更喜欢简单而不是优化。
【解决方案3】:

请注意,在当前版本的 Swift 中,以下解决方案已在标准库中实现,并且所有数学函数都为 DoubleFloatCGFloat 正确重载。

Ceil 是算术运算,与任何其他算术运算一样,DoubleFloat 都应该有一个重载版本。

var f1: Float = 1.0
var f2: Float = 2.0

var d1: Double = 1.0
var d2: Double = 2.0

var f = f1 + f2
var d = d1 + d2

这是因为+ 被重载并且适用于两种类型。

不幸的是,通过从不支持函数重载的 C 库中提取数学函数,我们只剩下两个函数而不是一个 - ceilceilf

我认为最好的解决方案是为Float 类型重载ceil

func ceil(f: CFloat) -> CFloat {
    return ceilf(f)
}

允许我们这样做:

var f: Float = 0.5
var d: Double = 0.5

var f: Float = ceil(f)
var d: Double = ceil(d)

一旦我们为FloatDouble 定义了相同的操作,即使CGFloat 的处理也会简单得多。

回答评论:

根据目标处理器架构,CGFloat 可以定义为FloatDouble。这意味着我们应该根据目标架构使用ceilceilf

var cgFloat: CGFloat = 1.5

//on 64bit it's a Double
var rounded: CGFloat = ceil(cgFloat)

//on 32bit it's a Float
var rounded: CGFloat = ceilf(cgFloat)

但是,我们将不得不使用丑陋的#if

另一种选择是使用巧妙的演员表

var cgFloat: CGFloat = 1.5
var rounded: CGFloat = CGFloat(ceil(Double(cgFloat))

(先转换为Double,然后将结果转换为CGFloat

但是,当我们处理数字时,我们希望数学函数是透明的。

var cgFloat1: CGFloat = 1.5
var cgFloat2: CGFloat = 2.5

// this works on both 32 and 64bit architectures!
var sum: CGFloat = cgFloat1 + cgFloat 2

如果我们重载ceilFloat 如上所示,我们可以做到

var cgFloat: CGFloat = 1.5

// this works on both 32 and 64bit architectures!
var rounded: CGFloat = ceil(cgFloat)

【讨论】:

  • 您愿意扩展 CGFloat 处理吗?这就是我特别感兴趣的,它具有 Float 和 Double 所不具备的复杂性,因为它既是双精度 又是 浮点数。
  • @MattGibson 添加了更多信息。 CGFloat 并不复杂,问题是我们缺少真正的 Swift 数学库。如果没有预处理器,C 函数就不能很好地工作。我们需要函数重载,而不是不同类型的不同函数。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-05-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-01-29
  • 2011-02-10
  • 2021-04-23
相关资源
最近更新 更多