这是一个简单的 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])} (我选择这个例子是因为很明显,你可能会调用数十
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)如果上面问题的答案是“no,c和’k’是在每次调用test时计算的”,那么在这种情况下如何在函数中放置一种静态计算属性?
不,在您的特定情况下,编译器当前不会将c和/或k优化为仅评估一次的常量表达式(在优化的构建中可以通过 examining the IR看到) – 尽管这可能会随着未来版本的变化而变化的语言.但值得注意的是,它目前可以为更简单的表达式执行此操作,例如:
func foo() -> Int { return 2 + 4 }
编译器可以在编译时评估添加,因此函数只返回6(然后可以内联).但是,当然,如果您确实将给定函数确定为性能瓶颈,那么您应该首先担心这种优化.
在函数中获取静态常量的一个很好的技巧是在函数作用域中定义一个带有静态属性的无壳枚举,您可以在其中定义常量表达式:
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]) }
现在,c和k的初始化表达式只会被评估一次,并且这将在它们首次使用时完成(即首次调用函数时).
当然,正如您所示,您可以使用立即评估的闭包来获得多行初始化表达式:
enum Constants { static let c: [Character] = { // ... return Array("abc".characters) }() // ... }