Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Example of dispatch_once in Swift

Is there an example of how dispatch_once should be used in Swift? (Preferably one from Apple.)

Note: In this case, I'm not using it for a singleton; I want to run arbitrary code exactly once.

Update: I'm mainly interested in the convention recommended when using this in an instance method, but usage in a class method, function, and in the global context would be useful for completeness sake.

like image 400
Senseful Avatar asked Jul 05 '15 22:07

Senseful


People also ask

What is Dispatch_once in Swift?

Executes a block object only once for the lifetime of an application.

What is the use of Dispatch_once?

This function is useful for initialization of global data (singletons) in an application. Always call this function before using or testing any variables that are initialized by the block. If called simultaneously from multiple threads, this function waits synchronously until the block has completed.

Is Dispatch_once thread safe?

dispatch_once() executes a block once and only once in a thread safe manner. Different threads that try to access the critical section — the code passed to dispatch_once — while a thread is already in this section are blocked until the critical section completes.

What is Dispatch_once_t?

Grand Central Dispatch, a.k.a libdispatch and usually referred to as GCD, is a low-level API known for performing asynchronous background work.


1 Answers

dispatch_once_t is type alias (Int). Header documentation:

/*!
 * @typedef dispatch_once_t
 *
 * @abstract
 * A predicate for use with dispatch_once(). It must be initialized to zero.
 * Note: static and global variables default to zero.
 */
typealias dispatch_once_t = Int

And here's the quote from dispatch_once documentation:

The predicate must point to a variable stored in global or static scope. The result of using a predicate with automatic or dynamic storage (including Objective-C instance variables) is undefined.

Token variable must be stored in global / static scope and must be initialized to zero, which leads to this code:

import Foundation

var token: dispatch_once_t = 0
dispatch_once(&token) { () -> Void in
  print("Called once")
}

It doesn't work if you omit = 0 (token initialization), because compiler yields error Address of variable 'token' taken before it is initialized despite the fact that statics and globals default to zero. Tested in Xcode 7B2.


More examples based on comment. If you're inside class method you have several possibilities.

You can't declare static property inside method otherwise compiler yields Static properties may only be declared on a type error. This doesn't work:

class func doItOnce() {
  static var token: dispatch_once_t = 0
  ...
}

Must be declared on a type. This was introduced in Swift 1.2 (Xcode 6.3 IIRC).

“static” methods and properties are now allowed in classes (as an alias for “class final”). You are now allowed to declare static stored properties in classes, which have global storage and are lazily initialized on first access (like global variables). Protocols now declare type requirements as “static” requirements instead of declaring them as “class” requirements. (17198298)

So, what we can do if we don't like globals?

Static variable on a type

class MyClass {
  private static var token: dispatch_once_t = 0

  class func doItOnce() {
    dispatch_once(&token) {
      print("Do it once")
    }
  }
}

Static in a method wrapped in struct

Don't like static property on yur class? Would like to have it in your method? Wrap it in struct like this:

class func doItOnce() {
  struct Tokens { static var token: dispatch_once_t = 0 }
  dispatch_once(&Tokens.token) {
    print("Do it once")
  }
}

Actually I'm not aware of any Apple recommendation, best practice, ... how to do it for dispatch_once. Simply use whatever you like most, feels good to you and just meet criteria global / static scope.

like image 200
zrzka Avatar answered Oct 14 '22 16:10

zrzka