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 vais avoir du plaisir à l'aide de DispatchQueue.main.async
, et j'ai été la navigation autour de la 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.")
}
}
Vous devez vous connecter pour publier un commentaire.
Depuis Swift 1.x, Swift a été à l'aide de
dispatch_once
derrière les coulisses pour effectuer thread-safe initialisation des variables globales et des propriétés statiques.De sorte que le
static var
ci-dessus était déjà à l'aide dedispatch_once
, ce qui le rend un peu bizarre (et éventuellement problématiques de l'utiliser à nouveau comme un jeton pour un autredispatch_once
. En fait, il n'y a vraiment pas de moyen sûr d'utiliserdispatch_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:Donc c'est génial si vous avez été en utilisant
dispatch_once
pour un temps initialisation résultats dans certaines de 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 variableVoid
: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:
Voir le guide de migration pour plus d'.
private
à la classe des constructeurs? Notez que si vous avez une variable dans cette classe, vous pourriez avoir plusieurs instances de la tenue d'une autre valeur pour cette variable. Une fois que le constructeur estprivate
, seul lestatic let singleton
ligne serait en mesure de l'initialiser.dispatch_once
bloc?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
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.Tandis 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 le doc 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:
Fondamentalement, je suis en train de dire que "Quand j'ai lu cela,
foo
devrait être le résultat de l'exécution de ce bloc." Il se comporte exactement de la même manière mondiallet
constante aurait, juste dans la bonne 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_
pour que. Comme:Ce vraiment cool 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 dansfoo
.Void
utilise pratiquement rien de la mémoire-sage, de sorte que les lectures suivantes (c'est à dire_ = 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 cette vieilledispatch_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
à autre chose avant de lire d'abord, puis votre code jamais être appelé! D'où le mondiallet
constant, qui l'en empêche. Chose est, des constantes dans la classe étendue de ne pas bien jouer avecself
, donc pas de jouer avec les variables d'instance... Mais sérieusement, quand avez-vous mis rien àVoid
de toute façon??Que, et vous avez besoin de spécifier le type de retour que
Void
ou()
, sinon il va encore se plaindreself
. Qui da thunk?Et
lazy
est juste pour faire de la variable différée, comme il se doit, donc, Swift ne pas l'exécuter directement surinit()
.Assez snazzy, aussi longtemps que vous vous souvenez de ne pas écrire! 😛
Tandis que les "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
dispatch_once()
a été, je n'aime pas à quoi il ressemble au site d'appel: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:
Qui rend possibles suivantes:
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.Void
: si quelqu'un écritdoSomethingOnce = {}
avant le premier appel, il ne sera pas l'appeler encore une fois.try
mot-clé, tout comme normal lancer des fonctions (j'ai vérifié cette rapide 4.2).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 Image De L'Échantillon
Étape 2 : Appel Singleton de ViewController.swift
ViewController Image De L'Échantillon
Sortie comme celle-ci
Compile sous Xcode 8 GA Swift 3
Le recommandé et élégant pour créer une dispatch_once singleton instance de classe:
Pour l'utiliser:
Que faire de ces lignes n'?
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.
Selon la Guide De Migration:
Exemple:
Thread-safe dispatch_once:
private lazy var foo: Void = { print("hello") }()
ce code affichera "bonjour" à plusieurs reprises (reproduit sur simulateur uniquement):for _ in 0 ... 1000 { DispatchQueue.global(qos: .default).async { _ = self.foo } } }