93

What is the new syntax for dispatch_once in Swift after the changes made in language version 3? The old version was as follows.

var token: dispatch_once_t = 0
func test() {
    dispatch_once(&token) {
    }
}

These are the changes to libdispatch that were made.

jscs
  • 63,694
  • 13
  • 151
  • 195
David
  • 14,205
  • 20
  • 97
  • 144
  • Possible duplicate: [Whither dispatch_once in Swift 3?](http://stackoverflow.com/q/37801407/957768) – rickster Jun 18 '16 at 02:03
  • Based on the answers https://stackoverflow.com/a/38311178/1648724 and https://stackoverflow.com/a/39983813/1648724 , I created a CocoaPod to do this: [`pod 'SwiftDispatchOnce', '~> 1.0'`](https://github.com/JRG-Developer/SwiftDispatchOnce) Cheers. :] – JRG-Developer Aug 09 '17 at 23:24

12 Answers12

109

While using lazy initialized globals can make sense for some one time initialization, it doesn't make sense for other types. It makes a lot of sense to use lazy initialized globals for things like singletons, it doesn't make a lot of sense for things like guarding a swizzle setup.

Here is a Swift 3 style implementation of dispatch_once:

public extension DispatchQueue {

    private static var _onceTracker = [String]()

    /**
     Executes a block of code, associated with a unique token, only once.  The code is thread safe and will
     only execute the code once even in the presence of multithreaded calls.

     - parameter token: A unique reverse DNS style name such as com.vectorform.<name> or a GUID
     - parameter block: Block to execute once
     */
    public class func once(token: String, block:@noescape(Void)->Void) {
        objc_sync_enter(self); defer { objc_sync_exit(self) }

        if _onceTracker.contains(token) {
            return
        }

        _onceTracker.append(token)
        block()
    }
}

Here is an example usage:

DispatchQueue.once(token: "com.vectorform.test") {
    print( "Do This Once!" )
}

or using a UUID

private let _onceToken = NSUUID().uuidString

DispatchQueue.once(token: _onceToken) {
    print( "Do This Once!" )
}

As we are currently in a time of transition from swift 2 to 3, here is an example swift 2 implementation:

public class Dispatch
{
    private static var _onceTokenTracker = [String]()

    /**
     Executes a block of code, associated with a unique token, only once.  The code is thread safe and will
     only execute the code once even in the presence of multithreaded calls.

     - parameter token: A unique reverse DNS style name such as com.vectorform.<name> or a GUID
     - parameter block: Block to execute once
     */
    public class func once(token token: String, @noescape block:dispatch_block_t) {
        objc_sync_enter(self); defer { objc_sync_exit(self) }

        if _onceTokenTracker.contains(token) {
            return
        }

        _onceTokenTracker.append(token)
        block()
    }

}
Tod Cunningham
  • 3,691
  • 4
  • 30
  • 32
77

From the doc:

Dispatch
The free function dispatch_once is no longer available in Swift. In Swift, you can use lazily initialized globals or static properties and get the same thread-safety and called-once guarantees as dispatch_once provided. Example:

let myGlobal: () = { … global contains initialization in a call to a closure … }()
_ = myGlobal  // using myGlobal will invoke the initialization code only the first time it is used.
Josh Bernfeld
  • 4,246
  • 2
  • 32
  • 35
Mtoklitz113
  • 3,828
  • 3
  • 21
  • 40
  • 3
    It's not as if you didn't know that Swift would be changing rapidly and you would have to correct a lot of broken code between versions of Swift. – Abizern Oct 13 '16 at 10:32
  • 2
    the biggest pain is the 3rd party pods which are not always Swift3 compatible. – Tinkerbell Oct 13 '16 at 13:00
  • 4
    That's the technical debt you accrue when introducing third party dependencies, @Tinkerbell. I love Swift but am extra cautious bringing in external dependencies that use it for this very reason. – Chris Wagner Oct 14 '16 at 17:50
  • 24
    `dispatch_once` was clear. This, unfortunately, is ugly and confusing.. – Alexandre G Oct 19 '18 at 02:23
68

Expanding on Tod Cunningham's answer above, I've added another method which makes the token automatically from file, function, and line.

public extension DispatchQueue {
    private static var _onceTracker = [String]()
    
    public class func once(
        file: String = #file,
        function: String = #function,
        line: Int = #line,
        block: () -> Void
    ) {
        let token = "\(file):\(function):\(line)"
        once(token: token, block: block)
    }
    
    /**
     Executes a block of code, associated with a unique token, only once.  The code is thread safe and will
     only execute the code once even in the presence of multithreaded calls.
     
     - parameter token: A unique reverse DNS style name such as com.vectorform.<name> or a GUID
     - parameter block: Block to execute once
     */
    public class func once(
        token: String,
        block: () -> Void
    ) {
        objc_sync_enter(self)
        defer { objc_sync_exit(self) }
        
        guard !_onceTracker.contains(token) else { return }
        
        _onceTracker.append(token)
        block()
    }
}

So it can be simpler to call:

DispatchQueue.once {
    setupUI()
}

and you can still specify a token if you wish:

DispatchQueue.once(token: "com.hostname.project") {
    setupUI()
}

I suppose you could get a collision if you have the same file in two modules. Too bad there isn't #module

VaporwareWolf
  • 10,143
  • 10
  • 54
  • 80
21

Edit

@Frizlab's answer - this solution is not guaranteed to be thread-safe. An alternative should be used if this is crucial

Simple solution is

lazy var dispatchOnce : Void  = { // or anyName I choose

    self.title = "Hello Lazy Guy"

    return
}()

used like

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    _ = dispatchOnce
}
Ryan Heitner
  • 13,119
  • 6
  • 77
  • 119
  • 1
    This doesn't help at all, because a lazy var declaration cannot be made inline with regular code, it has to be in a struct or class definition. That means the contents of the dispatchOnce cannot capture surrounding scope of an instance. For example if you declare a closure that has not run yet, you cannot then declare the struct inside that closure and have the lazy var's contents be another closure that captures vars from the surrounding closure... – CommaToast Jul 24 '17 at 19:56
  • 3
    Downvoted because this code has definitely **not** the same semantics as dispatch_once. dispatch_once ensures the code is run exactly once, **whichever thread you call it from**. Lazy vars have undefined behavior in a multi-threaded environment. – Frizlab Aug 08 '17 at 09:34
  • in this solution init block is going to call twice in some cases – sacred0x01 Oct 22 '18 at 10:49
11

You can declare a top-level variable function like this:

private var doOnce: ()->() = {
    /* do some work only once per instance */
    return {}
}()

then call this anywhere:

doOnce()
Ky -
  • 30,724
  • 51
  • 192
  • 308
Bogdan Novikov
  • 565
  • 6
  • 14
  • 2
    Lazy vars are scoped to the class, so this absolutely will not act like dispatch_once. It will execute once per instance of the underlying class. Either move it outside the class [ private var doOnce: ()->() = { } ] or mark it static [ static private var doOnce: ()->() = { } ] – Eli Burke Jul 31 '18 at 15:17
  • 1
    Absolutely correct! Thanks. In most cases you'll need once action per instance. – Bogdan Novikov Aug 09 '18 at 11:09
  • 2
    This is a really great solution! Elegant, short, and clear – Ky - Jan 22 '19 at 19:18
9

You can still use it if you add a bridging header:

typedef dispatch_once_t mxcl_dispatch_once_t;
void mxcl_dispatch_once(mxcl_dispatch_once_t *predicate, dispatch_block_t block);

Then in a .m somewhere:

void mxcl_dispatch_once(mxcl_dispatch_once_t *predicate, dispatch_block_t block) {
    dispatch_once(predicate, block);
}

You should now be able to use mxcl_dispatch_once from Swift.

Mostly you should use what Apple suggest instead, but I had some legitimate uses where I needed to dispatch_once with a single token in two functions and there is not covered by what Apple provide instead.

mxcl
  • 26,392
  • 12
  • 99
  • 98
7

Swift 3: For those who likes reusable classes (or structures):

public final class /* struct */ DispatchOnce {
   private var lock: OSSpinLock = OS_SPINLOCK_INIT
   private var isInitialized = false
   public /* mutating */ func perform(block: (Void) -> Void) {
      OSSpinLockLock(&lock)
      if !isInitialized {
         block()
         isInitialized = true
      }
      OSSpinLockUnlock(&lock)
   }
}

Usage:

class MyViewController: UIViewController {

   private let /* var */ setUpOnce = DispatchOnce()

   override func viewWillAppear() {
      super.viewWillAppear()
      setUpOnce.perform {
         // Do some work here
         // ...
      }
   }

}

Update (28 April 2017): OSSpinLock replaced with os_unfair_lock due deprecation warnings in macOS SDK 10.12.

public final class /* struct */ DispatchOnce {
   private var lock = os_unfair_lock()
   private var isInitialized = false
   public /* mutating */ func perform(block: (Void) -> Void) {
      os_unfair_lock_lock(&lock)
      if !isInitialized {
         block()
         isInitialized = true
      }
      os_unfair_lock_unlock(&lock)
   }
}
Vlad
  • 6,402
  • 1
  • 60
  • 74
  • I get a message that OSSSpinLock is deprecated in iOS 10.0 – markhorrocks Apr 28 '17 at 10:15
  • 2
    Thanks! Example code updated. `OSSpinLock` replaced with `os_unfair_lock`. BTW: Here is a good WWDC video about `Concurrent Programming`: https://developer.apple.com/videos/play/wwdc2016/720/ – Vlad Apr 28 '17 at 15:30
3

Swift provides the easiest way to achieve that by declaring lazy initialized property, it is guaranteed to be run only once. All we want to do is just read the property at needed moment.

 lazy var executeOnce: Void = {
      print("")
  }()

  for 0...100 {
    _ = executeOnce  // will run only once we access it for first time
  }
kefir
  • 21
  • 4
0

I improve above answers get result:

import Foundation
extension DispatchQueue {
    private static var _onceTracker = [AnyHashable]()

    ///only excute once in same file&&func&&line
    public class func onceInLocation(file: String = #file,
                           function: String = #function,
                           line: Int = #line,
                           block: () -> Void) {
        let token = "\(file):\(function):\(line)"
        once(token: token, block: block)
    }

    ///only excute once in same Variable
    public class func onceInVariable(variable:NSObject, block: () -> Void){
        once(token: variable.rawPointer, block: block)
    }
    /**
     Executes a block of code, associated with a unique token, only once.  The code is thread safe and will
     only execute the code once even in the presence of multithreaded calls.

     - parameter token: A unique reverse DNS style name such as com.vectorform.<name> or a GUID
     - parameter block: Block to execute once
     */
    public class func once(token: AnyHashable,block: () -> Void) {
        objc_sync_enter(self)
        defer { objc_sync_exit(self) }

        guard !_onceTracker.contains(token) else { return }

        _onceTracker.append(token)
        block()
    }

}

extension NSObject {
    public var rawPointer:UnsafeMutableRawPointer? {
        get {
            Unmanaged.passUnretained(self).toOpaque()
        }
    }
}
leonardosccd
  • 1,503
  • 11
  • 12
0
import UIKit

  // dispatch once
   class StaticOnceTest {
    
    static let test2 = {
        print("Test " + $0 + " \($1)")
    }("mediaHSL", 5)
    
    lazy var closure: () = {
        test(entryPoint: $0, videos: $1)
    }("see all" , 4)
    
    private func test(entryPoint: String, videos: Int) {
        print("Test " + entryPoint + " \(videos)")
    }
    }

print("Test-1")
let a = StaticOnceTest()
a.closure
a.closure
a.closure
a.closure
StaticOnceTest.test2
StaticOnceTest.test2
StaticOnceTest.test2
StaticOnceTest.test2

OUTPUT:

Test-1
Test see all 4
Test mediaHSL 5

You can use a lazy var closure and execute it immediately with (#arguments_if_needed) so that it will call only one time. You can call any instance function inside of the closure [advantage].

You can pass multiple arguments based on need. You can capture those arguments when the class has been initialised and use them.

Another option: You can use a static let closure and it will execute only one time but you cannot call any instance func inside that static let clsoure. [disadvantage]

thanks!

Ashis Laha
  • 2,176
  • 3
  • 16
  • 17
0

Swift 5

dispatch_once is still available in libswiftFoundation.dylib standard library which is embedded to any swift app so you can access to exported symbols dynamically, get the function's symbol pointer, cast and call:

import Darwin

typealias DispatchOnce = @convention(c) (
    _ predicate: UnsafePointer<UInt>?,
    _ block: () -> Void
) -> Void

func dispatchOnce(_ predicate: UnsafePointer<UInt>?, _ block: () -> Void) {
    let RTLD_DEFAULT = UnsafeMutableRawPointer(bitPattern: -2)
    
    if let sym = dlsym(RTLD_DEFAULT, "dispatch_once") {
        let f = unsafeBitCast(sym, to: DispatchOnce.self)
        f(predicate, block)
    }
    else {
        fatalError("Symbol not found")
    }
}

Example:

var token: UInt = 0

for i in 0...10 {
    print("iteration: \(i)")
    
    dispatchOnce(&token) {
      print("This is printed only on the first call")
    }
}

Outputs:

iteration: 0
This is printed only on the first call
iteration: 1
iteration: 2
iteration: 3
iteration: 4
iteration: 5
iteration: 6
iteration: 7
iteration: 8
iteration: 9
iteration: 10
iUrii
  • 11,742
  • 1
  • 33
  • 48
-3

Use the class constant approach if you are using Swift 1.2 or above and the nested struct approach if you need to support earlier versions. An exploration of the Singleton pattern in Swift. All approaches below support lazy initialization and thread safety. dispatch_once approach is not worked in Swift 3.0

Approach A: Class constant

class SingletonA {

    static let sharedInstance = SingletonA()

    init() {
        println("AAA");
    }

}

Approach B: Nested struct

class SingletonB {

    class var sharedInstance: SingletonB {
        struct Static {
            static let instance: SingletonB = SingletonB()
        }
        return Static.instance
    }

}

Approach C: dispatch_once

class SingletonC {

    class var sharedInstance: SingletonC {
        struct Static {
            static var onceToken: dispatch_once_t = 0
            static var instance: SingletonC? = nil
        }
        dispatch_once(&Static.onceToken) {
            Static.instance = SingletonC()
        }
        return Static.instance!
    }
}
Ky -
  • 30,724
  • 51
  • 192
  • 308
Shan Shafiq
  • 1,018
  • 10
  • 10