How do I execute code once and only once in Swift?

北慕城南 提交于 2019-11-28 09:50:38

Static properties initialized by a closure are run lazily and at most once, so this prints only once, in spite of being called twice:

/*
run like:

    swift once.swift
    swift once.swift run

to see both cases
*/
class Once {
    static let run: Void = {
        print("Behold! \(__FUNCTION__) runs!")
        return ()
    }()
}

if Process.arguments.indexOf("run") != nil {
    let _ = Once.run
    let _ = Once.run
    print("Called twice, but only printed \"Behold\" once, as desired.")
} else {
    print("Note how it's run lazily, so you won't see the \"Behold\" text now.")
}

Example runs:

~/W/WhenDoesStaticDefaultRun> swift once.swift
Note how it's run lazily, so you won't see the "Behold" text now.
~/W/WhenDoesStaticDefaultRun> swift once.swift run
Behold! Once runs!
Called twice, but only printed "Behold" once, as desired.

A man went to the doctor, and said "Doctor, it hurts when I stamp on my foot". The doctor replied, "So stop doing it".

If you deliberately alter your dispatch token, then yes - you'll be able to execute the code twice. But if you work around the logic designed to prevent multiple execution in any way, you'll be able to do it. dispatch_once is still the best method to ensure code is only executed once, as it handles all the (very) complex corner cases around initialisation and race conditions that a simple boolean won't cover.

If you're worried that someone might accidentally reset the token, you can wrap it up in a method and make it as obvious as it can be what the consequences are. Something like the following will scope the token to the method, and prevent anyone from changing it without serious effort:

func willRunOnce() -> () {
    struct TokenContainer {
        static var token : dispatch_once_t = 0
    }

    dispatch_once(&TokenContainer.token) {
        print("This is printed only on the first call")
    }
}

I think the best approach is to just construct resources lazily as needed. Swift makes this easy.

There are several options. As already mentioned, you can initialize a static property within a type using a closure.

However, the simplest option is to define a global variable (or constant) and initialize it with a closure then reference that variable anywhere the initialization code is required to have happened once:

let resourceInit : Void = {
  print("doing once...")
  // do something once
}()

Another option is to wrap the type within a function so it reads better when calling. For example:

func doOnce() {
    struct Resource {
        static var resourceInit : Void = {
            print("doing something once...")
        }()
    }

    let _ = Resource.resourceInit
}

You can do variations on this as needed. For example, instead of using the type internal to the function, you can use a private global and internal or public function as needed.

However, I think the best approach is just to determine what resources you need to initialize and create them lazily as global or static properties.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!