Skip to content

Instantly share code, notes, and snippets.

@rnapier
Last active April 20, 2022 16:12
Show Gist options
  • Save rnapier/417153734150208dc8a18e3d6318255e to your computer and use it in GitHub Desktop.
Save rnapier/417153734150208dc8a18e3d6318255e to your computer and use it in GitHub Desktop.
A function that only executes once. Good Swift or over-clever?
// 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.
@mpw
Copy link

mpw commented Nov 5, 2016

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

@ptrkstr
Copy link

ptrkstr commented Aug 16, 2018

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

@claybridges
Copy link

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