【问题标题】:Swift constants (with a calculation) in functions?函数中的 Swift 常量(带计算)?
【发布时间】:2017-10-04 22:22:06
【问题描述】:

这是一个简单的 Swift 函数

fileprivate func test()->String{
    let c = Array("abc".characters)
    let k = UInt32(c.count)
    let r = Int(arc4random_uniform(k))
    return String(c[r])
}

(我之所以选择这个示例,显然是因为您可能会调用数十亿次来生成某种输出;因此您可能会担心设置这两个常量时的性能。)

请注意,要获得c,必须进行一些计算,实际上要获得k,它必须使用c

我的问题很简单:每次调用这个函数

test()
test()
test()

事实上,它会计算k 和/或c每次我调用它时, 还是它们只计算一次

(如果“只有一次”,那么出于好奇:我第一次调用该函数时它会这样做吗?或者编译器可能会安排在启动时单独完成它?或者它是否知道它可以计算他们在编译期间?)


我经常使用全局计算属性,比较喜欢这样

let basicDF : DateFormatter = {
    print("this will only be done once per launch of the app")
    let formatter = DateFormatter()
    formatter.dateFormat = "yyyy-MM-dd"
    return formatter 
}()

(也许是fileprivate)如果上面问题的答案是“不,c 和'k' 每次调用测试时都会计算”,那么在这种情况下你怎么能把一种静态计算函数内的属性??

【问题讨论】:

    标签: swift static computed-properties


    【解决方案1】:

    不,在您的特定情况下,编译器当前不会将 c 和/或 k 优化为仅计算一次的常量表达式(这可以通过 examining the IR 在优化构建中看到)——尽管这一切都可能随着语言的未来版本而改变。

    但值得注意的是,它目前可以为更简单的表达式执行此操作,例如:

    func foo() -> Int {
        return 2 + 4
    }
    

    编译器可以在编译时评估加法,因此函数只执行return 6(然后可以内联)。但是,当然,如果您确实将给定函数确定为性能瓶颈,则首先应该担心此类优化。

    在函数中获取静态常量的一个好技巧是在函数范围内定义具有静态属性的无大小写 enum,您可以在其中定义常量表达式:

    func test() -> String {
    
        enum Constants {
            static let c = Array("abc".characters)
            static let k = UInt32(c.count)
        }
    
        let r = Int(arc4random_uniform(Constants.k))
        return String(Constants.c[r])
    }
    

    现在ck 的初始化表达式都只会被计算一次,这将在它们第一次使用时完成(即第一次调用函数时)。

    当然,正如您所展示的,您可以使用立即评估的闭包来获得多行初始化表达式:

    enum Constants {
        static let c: [Character] = {
            // ...
            return Array("abc".characters)
        }()
        // ...
    }
    

    【讨论】:

    • 太棒了,所以在 Swift 中做静态是枚举静态的。太棒了,谢谢。来自位码的决定性信息,谢谢。
    【解决方案2】:

    我认为你应该假设ck 每次都会被计算。计算可能会被优化为编译器的实现细节,但如果我是你,我不会指望它。

    Swift 没有等效于 C 的 static 局部变量(即函数内的“自动”变量,其值在函数调用之间保持不变)。

    如果您真的想努力确保 k 只计算一次,请将其设为真正的常量(即在类级别)。当然,您还必须对c 执行相同的操作,因为您在以后的计算中需要它。在这种情况下,这似乎是一个愚蠢的例子,但是当正在创建的东西是重量级时,我经常这样做,例如将被反复使用的图像或视图:

    class MyView : UIView {
        lazy var arrow : UIImage = self.arrowImage()
        func arrowImage () -> UIImage {
            // ... big image-generating code goes here ...
        }
    }
    

    我一遍又一遍地调用arrowImage(),直到我对自己说,等等,我可以把它设为一个常数(这里表示为lazy var)并只计算一次,即第一次访问arrow .

    【讨论】:

    • 我明白了,所以“lazy var”是类级别的解决方案(lazy var c = Array("abc".characters),就这么简单);不能在函数中做。很棒的信息谢谢。
    猜你喜欢
    • 2014-12-28
    • 1970-01-01
    • 2015-09-30
    • 1970-01-01
    • 1970-01-01
    • 2021-07-24
    • 2011-07-19
    • 2014-07-24
    相关资源
    最近更新 更多