Last active
April 20, 2022 16:12
-
-
Save rnapier/417153734150208dc8a18e3d6318255e to your computer and use it in GitHub Desktop.
A function that only executes once. Good Swift or over-clever?
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Swift3 gets rid of dispatch_once and recommends replacing it with a lazy global. | |
// That's very straightforward when dispach_once is used to initialize something, but | |
// isn't an exact match when you want something to execute once, and then become a noop | |
// in a thread-safe way. | |
// The following approach seems completely "correct" and I guess actually a bit elegant, | |
// if by "elegant" you mean "terse and not immediately obvious to the reader, which makes | |
// you look very clever." | |
var doOnce: () -> Void = { | |
// The side effect that should only happen once | |
print("Running once, I hope") | |
// A dummy function that's evaluated every time we're accessed | |
return {} | |
}() | |
doOnce() // Prints | |
doOnce() // does not print | |
doOnce() // does not print | |
// So is this good Swift that readers should simply learn to understand, or is it over-clever Swift | |
// that has a better answer that is both idempotent and threadsafe? | |
// I know the docs suggest just "evaluating a var with _ =" but that seems horrible compared to having a clear | |
// function-call syntax. |
Hmm...but this runs the print() before the first invocation of doOnce(). Is that the way it is intended?
var doOnce: () -> Void = {
// The side effect that should only happen once
print("Running once, I hope")
// A dummy function that's evaluated every time we're accessed
return {}
}()
print("before")
doOnce()
doOnce()
doOnce()
marcel@sarek[tmp]./once
Running once, I hope
before
@mpw that looks like it's running as intended.
To simplify it I recommend the following typealias.
/// Useful when wanting to initialise something only once.
/// Use `return {}` at the end of the closure.
/// Example usage:
/// ```
/// static var initialize: PerformOnce = {
/// print("hello world")
/// return {}
/// }()
/// ```
typealias PerformOnce = () -> Void
Usage
struct AwesomeStruct {
static var initialize: PerformOnce = {
Print("Hello World")
return {}
}()
}
Reinforcing @mpw, this code as-is prints on var instantiation, before even the first doOnce()
call.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Note that Swift global vars are lazy, but vars in the IBM Sandbox (https://swiftlang.ng.bluemix.net/#/repl) are not global. If you execute this code in the sandbox, "Running once" is indeed printed only once, but it's printed at line 16, not line 18. This difference could be significant for people trying to ensure that the initialization happens on the first call to the function and not before. People can see the difference in behavior by adding a print to the dummy inner function.