Whither dispatch_once in Swift 3?

前端 未结 7 1601
再見小時候
再見小時候 2020-11-27 04:16

Okay, so I found out about the new Swifty Dispatch API in Xcode 8. I\'m having fun using DispatchQueue.main.async, and I\'ve been browsing around the Disp

相关标签:
7条回答
  • 2020-11-27 04:49

    Thread-safe dispatch_once:

    private lazy var foo: Void = {
        objc_sync_enter(self)
        defer { objc_sync_exit(self) }
    
        // Do this once
    }()
    
    0 讨论(0)
  • 2020-11-27 04:50

    Compiles under Xcode 8 GA Swift 3

    The recommended and elegant way to create a dispatch_once singleton class instance:

    final class TheRoot {
    static let shared = TheRoot()
    var appState : AppState = .normal
    ...
    

    To use it:

    if TheRoot.shared.appState == .normal {
    ...
    }
    

    What do these lines do?

    final - so the class cannot be overridden, extended, it also makes the code somewhat faster to run, less indirections.

    static let shared = TheRoot() - This line does lazy init, and it only runs once.

    This solution is thread safe.

    0 讨论(0)
  • 2020-11-27 04:53

    While the "lazy var" pattern allows me to stop caring about dispatch tokens and is generally more convenient than dispatch_once() was, I don't like how it looks at call site:

    _ = doSomethingOnce
    

    I would expect this statement to look more like a function call (since it implies action), but it doesn't look so at all. Also, having to write _ = to explicitly discard the result is unnecessary and annoying.

    There is a better way:

    lazy var doSomethingOnce: () -> Void = {
      print("executed once")
      return {}
    }()
    

    Which makes the following possible:

    doSomethingOnce()
    

    This might be less efficient (since it calls an empty closure instead of just discarding a Void), but improved clarity is totally worth it for me.

    0 讨论(0)
  • 2020-11-27 04:54

    Since Swift 1.x, Swift has been using dispatch_once behind the scenes to perform thread-safe lazy initialization of global variables and static properties.

    So the static var above was already using dispatch_once, which makes it sort of weird (and possibly problematic to use it again as a token for another dispatch_once. In fact there's really no safe way to use dispatch_once without this kind of recursion, so they got rid of it. Instead, just use the language features built on it:

    // global constant: SomeClass initializer gets called lazily, only on first use
    let foo = SomeClass()
    
    // global var, same thing happens here
    // even though the "initializer" is an immediately invoked closure
    var bar: SomeClass = {
        let b = SomeClass()
        b.someProperty = "whatever"
        b.doSomeStuff()
        return b
    }()
    
    // ditto for static properties in classes/structures/enums
    class MyClass {
        static let singleton = MyClass()
        init() {
            print("foo")
        }
    }
    

    So that's all great if you've been using dispatch_once for one-time initialization that results in some value -- you can just make that value the global variable or static property you're initializing.

    But what if you're using dispatch_once to do work that doesn't necessarily have a result? You can still do that with a global variable or static property: just make that variable's type Void:

    let justAOneTimeThing: () = {
        print("Not coming back here.")
    }()
    

    And if accessing a global variable or static property to perform one-time work just doesn't feel right to you -- say, you want your clients to call an "initialize me" function before they work with your library -- just wrap that access in a function:

    func doTheOneTimeThing() {
        justAOneTimeThing
    }
    

    See the migration guide for more.

    0 讨论(0)
  • Example for "dispatch_once" in Swift 3.0

    Step 1 : Just Replace below code with your Singleton.swift (Singleton class)

    // Singleton Class
    class Singleton: NSObject { 
    var strSample = NSString()
    
    static let sharedInstance:Singleton = {
        let instance = Singleton ()
        return instance
    } ()
    
    // MARK: Init
     override init() {
        print("My Class Initialized")
        // initialized with variable or property
        strSample = "My String"
    }
    }
    

    Singleton Sample Image

    Step 2 : Call Singleton from ViewController.swift

    // ViewController.swift
        override func viewDidLoad() {
            super.viewDidLoad()
            // Do any additional setup after loading the view, typically from a nib.
            let mySingleton = Singleton.sharedInstance
            print(mySingleton.strSample)
    
            mySingleton.strSample = "New String"
            
            print(mySingleton.strSample)
            
            let mySingleton1 = Singleton.sharedInstance
            print(mySingleton1.strSample)
    
        }
    

    ViewController Sample Image

    Output like this

    My Class Initialized
    My String
    New String
    New String
    
    0 讨论(0)
  • 2020-11-27 05:01

    According to the Migration Guide:

    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 … }()
    
      // using myGlobal will invoke the initialization code only the first time it is used.
      _ = myGlobal  
    
    0 讨论(0)
提交回复
热议问题