57 votes

Où dispatch_once dans Swift 3?

Ok, donc, j'ai découvert au sujet de la nouvelle Swifty Envoi de l'API dans Xcode 8. Je m'amuse à l'aide d' DispatchQueue.main.async, et j'ai été la navigation autour de l' Dispatch module dans Xcode pour trouver toutes les nouvelles Api.

Mais j'utilise aussi dispatch_once de faire en sorte que les choses comme singleton création et installation d'un temps n'est pas exécuté plus d'une fois (même dans un environnement multithread)... et dispatch_once est nulle part pour être trouvée dans la nouvelle Expédition module?

static var token: dispatch_once_t = 0
func whatDoYouHear() {
    print("All of this has happened before, and all of it will happen again.")
    dispatch_once(&token) {
        print("Except this part.")
    }
}

59voto

rickster Points 19870

Depuis Swift 1.x, Swift a été à l'aide d' dispatch_once derrière les coulisses pour effectuer thread-safe initialisation des variables globales et des propriétés statiques.

Si l' static var - dessus était déjà à l'aide de dispatch_once, ce qui le rend un peu bizarre (et éventuellement problématiques de l'utiliser à nouveau comme un jeton pour un autre dispatch_once. En fait, il n'y a vraiment pas de moyen sûr d'utiliser dispatch_once sans ce genre de récursivité, de sorte qu'ils de s'en débarrasser. Au lieu de cela, il suffit d'utiliser les fonctionnalités de langage construit sur elle:

// 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")
    }
}

Donc ça c'est super si vous avez été en utilisant dispatch_once pour un temps d'initialisation que les résultats de certains de la valeur -- vous pouvez simplement faire que de la valeur de la variable globale ou statique de la propriété vous êtes en cours d'initialisation.

Mais que faire si vous êtes en utilisant dispatch_once de faire un travail qui n'est pas nécessairement un résultat? Vous pouvez toujours le faire avec une variable globale ou statique de la propriété: il suffit de faire que le type de la variable Void:

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

Et si l'accès à une variable globale ou statique de la propriété pour effectuer un travail à temps juste ne se sent pas le droit de vous -- dire, vous voulez que vos clients de faire appel à un "initialiser moi, la fonction" avant de travailler avec votre bibliothèque -- simplement conclure que l'accès à une fonction:

func doTheOneTimeThing() {
    justAOneTimeThing
}

Voir le guide de migration pour plus d'.

20voto

deville Points 2004

Alors que le "paresseux var" modèle me permet de m'arrêter de se soucier de répartition des jetons et est généralement plus pratique que d' dispatch_once() a été, je n'aime pas à quoi il ressemble au site d'appel:

_ = doSomethingOnce

Je m'attends à cette déclaration ressemble plus à un appel de fonction (puisqu'elle implique l'action), mais il n'a pas l'air du tout le cas. Aussi, avoir à écrire _ = explicitement jeter le résultat est inutile et ennuyeux.

Il ya une meilleure façon:

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

Ce qui rend possibles suivantes:

doSomethingOnce()

Cela pourrait être moins efficace (car il demande un vide de fermeture au lieu de simplement les jeter un Void), mais l'amélioration de la clarté est en vaut la peine pour moi.

17voto

SeizeTheDay Points 535

Les autres réponses d'ici et de partout les interwebs sont assez grands, mais je sens que cette petite friandise doivent également être mentionnées:

La grande chose au sujet d' dispatch_once était de savoir comment optimisé, il a été, essentiellement nixing le code après le premier run, d'une manière que j'ai peine à comprendre, mais je suis raisonnablement sûr que ce serait beaucoup plus rapide que la création et le contrôle d'un (vrai) global jeton.

Alors que le jeton chose qui pourrait raisonnablement être mis en œuvre de Swift, d'avoir à déclarer encore une autre stockées boolean n'est pas tout que de grands. Pour ne pas mentionner thread-dangereux. Comme la doc le dit, vous devez utiliser un "initialisées mondiale." Ouais, mais pourquoi encombrer la portée globale, droit?

Jusqu'à ce que quelqu'un me convainc une meilleure méthode, j'ai tendance à déclarer mes faire-une fois à l'intérieur fermeture à la portée que je vais utiliser, ou plus raisonnablement proches s'y rapportant, comme suit:

private lazy var foo: Void = {
    // Do this once
}()

Fondamentalement, je suis en train de dire que "Quand j'ai lu cette, foo devrait être le résultat de l'exécution de ce bloc." Il se comporte exactement de la même manière en tant que global let constante, dans le droit de la portée. Et plus jolie. Puis je l'appellerais partout où je me tiens, par la lecture de quelque chose qui ne sera jamais utilisé autrement. J'aime Swift _ . Comme suit:

_ = foo

C'est vraiment cool de caprice a réellement été autour d'un certain temps, mais n'a pas vu beaucoup d'amour. Essentiellement, il quitte la variable seul au moment de l'exécution, comme une déplacée de fermeture, jusqu'à ce que quelque chose souhaite voir ses Void résultat. En lecture, elle appelle la fermeture, la jette loin et maintient son résultat en foo. Void utilise pratiquement rien de ce mémoire, les lectures suivantes (c - _ = foo) ne rien faire sur le CPU. (Ne pas citer de moi, quelqu'un veuillez vérifier l'assemblée pour être sûr!) Autant que vous le souhaitez, et Swift, fondamentalement, quitte à se soucier de ça après la première manche! Perdre que de vieilles dispatch_once_t, et de garder beaucoup de votre code d'aussi jolie que lorsque vous l'avez ouvert le jour de Noël!

Mon seul problème est que vous pouvez définir foo de quelque chose d'autre avant sa première lecture, puis votre code ne jamais être appelé! D'où le global let constante, ce qui l'en empêche. Chose est, des constantes dans la classe étendue de ne pas bien jouer avec self, donc pas de jouer avec les variables d'instance... Mais sérieusement, quand avez-vous mis quelque chose à l' Void de toute façon??

Que, et vous avez besoin de spécifier le type de retour en tant que Void ou (), sinon il va encore se plaindre self. Qui da thunk?

Et lazy , c'est juste pour faire de la variable différée, comme il se doit, donc, Swift ne pas l'exécuter directement sur init().

Assez snazzy, aussi longtemps que vous vous souvenez de ne pas écrire à! :P

8voto

Vivek Points 1218

Exemple pour "dispatch_once" dans Swift 3.0

Étape 1 : il suffit de Remplacer le code ci-dessous avec votre Singleton.swift (classe Singleton)

// 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 Image De L'Échantillon

Étape 2 : Appel Singleton de 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 Image De L'Échantillon

Sortie comme celle-ci

My Class Initialized
My String
New String
New String

6voto

t1ser Points 1167

Compile sous Xcode 8 GA Swift 3

Le recommandé et élégant pour créer une dispatch_once singleton instance de classe:

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

Pour l'utiliser:

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

Que faire de ces lignes?

final - si la classe ne peut pas être remplacée, étendu, il rend également le code un peu plus rapide à exécuter, à moins d'indirections.

statique laissez-shared = TheRoot() - Cette ligne ne paresseux init, et il ne fonctionne qu'une fois.

Cette solution est thread-safe.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X