Here's a simple Swift function
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
.
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?
(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??
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.
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). 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:
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 (i.e when the function is first called).
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)
}()
// ...
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With