简体   繁体   English

函数中的Swift常数(带有计算)?

[英]Swift constants (with a calculation) in functions?

Here's a simple Swift function 这是一个简单的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])
}

(I chose this example because, obviously, it's something you may call billions of times to generate some sort of output; so you might be concerned for performance in setting up the two constants.) (我选择此示例是因为,显然,您可能会调用数十亿次才能生成某种输出;因此您可能会担心设置两个常量时的性能。)

Note that it has to do a bit of calculation to get c , and indeed to get k it has to use c . 请注意,它必须做一些计算才能得到c ,而实际上要获得k则必须使用c

My question is simple: every time you call this function 我的问题很简单:每次调用此函数

test()
test()
test()

in fact does it calculate k , and/or c every time I call it, or indeed are they only calculated once ? 实际上, 我每次调用它时都会计算k和/或c 或者实际上它们仅计算一次吗?

(if "only once", then as a curiosity: it does that the first time I call the function? or perhaps the compiler arranges to have it done separately at startup time? or for that matter does it know it can compute them during compilation?) (如果“仅一次”,那么出于好奇:是我第一次调用该函数吗?或者编译器安排在启动时分别完成它?或者为此,它知道它可以在编译期间计算它们吗? ?)


I often use global calculated properties, rather like this 我经常使用全局计算的属性,就像这样

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 
}()

(perhaps with fileprivate ) If the answer to the above question is "no, c and 'k' are calculated every time you call test", then in that case how can you put a kind of static computed property inside a function?? (也许使用fileprivate )如果上述问题的答案是“否,则每次调用test时都会计算c和'k'”,那么在那种情况下,如何在函数内放置一种静态计算属性?

No, in your particular case, the compiler currently doesn't optimise c and/or k to constant expressions that are only evaluated once (this can be seen by examining the IR in an optimised build) – although this is all subject to change with future versions of the language. 不,在您的特定情况下,编译器当前不会将c和/或k优化为仅求值一次的常量表达式(这可以通过在优化的构建中检查IR来看出)–尽管这可能会随语言的未来版本。

However it's worth noting that it can currently do this for simpler expressions, for example: 但是,值得注意的是,它目前可以针对更简单的表达式执行此操作,例如:

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

The compiler can evaluate the addition at compile-time so the function just does return 6 (and then this can be inlined). 编译器可以在编译时评估加法,因此该函数仅return 6 (然后可以内联)。 But of course, you should only be worrying about such optimisations in the first place if you've actually identified the given function as a performance bottleneck. 但是,当然,如果您实际上已经将给定功能确定为性能瓶颈,则首先应该只担心这种优化。

One nice trick to get static constants in a function is to define a case-less enum with static properties in the function scope, in which you can define your constant expressions: 在函数中获取静态常量的一个不错的技巧是在函数范围内定义一个具有静态属性的无大小写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])
}

Now both the initialiser expressions for c and k will only be evaluated once, and this will be done when they are first used (ie when the function is first called). 现在, ck的初始化程序表达式都将只被评估一次,这将在首次使用它们时(即,在首次调用该函数时)进行。

And of course, as you show, you can use an immediately-evalutated closure in order to have a multi-line initialisation expression: 当然,正如您所展示的,您可以使用立即评估的闭包以具有多行初始化表达式:

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

I think you should assume that c and k are calculated every time. 我认为您应该假设每次都计算ck The calculation might be optimized away as an implementation detail of the compiler, but I wouldn't count on that if I were you. 可能会将计算优化为编译器的实现细节,但是如果您是我,我不会指望这一点。

Swift has no equivalent to a C static local variable (that is, an "automatic" variable within a function whose value is maintained between calls to the function). Swift没有等效于C static局部变量(即,函数内的“自动”变量,其值在两次调用之间保持不变)。

If you really want to put the effort into making sure that k is only calculated once, make it a true constant (ie at class level). 如果您真的想确保k仅计算一次,请使其为真正的常量(即在类级别)。 Of course you will then also have to do the same for c , as you need it in your later calculations. 当然,您还需要对c进行相同的操作,这在以后的计算中需要。 It seems like a silly example in this case, but I often do this when the thing being created is heavyweight, like an image or view that will be used over and over: 在这种情况下,这似乎是一个愚蠢的示例,但是当创建的东西很重时,例如在反复使用的图像或视图时,我经常这样做:

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

I was calling arrowImage() over and over until I said to myself, wait, I can make this a constant (here expressed as a lazy var ) and calculate it just once, namely the first time arrow is accessed. arrowImage()调用arrowImage() ,直到我对自己说,等等,我可以将其设为一个常数(在这里表示为lazy var )并仅对其进行一次计算,即第一次访问arrow

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM