2017-05-06 4 views
5

est ici une simple fonction SwiftConstantes rapides (avec un calcul) dans les fonctions?

fileprivate func test()->String{ 
    let c = Array("abc".characters) 
    let k = UInt32(c.count) 
    let r = Int(arc4random_uniform(k)) 
    return String(c[r]) 
} 

(j'ai choisi cet exemple parce que, évidemment, il est quelque chose que vous pouvez appeler des milliards de fois pour générer une sorte de sortie, de sorte que vous pourriez être concerné pour la performance dans la mise en place des deux constantes.)

Notez qu'il doit faire un peu de calcul pour obtenir c, et en effet pour obtenir k il doit utiliser c.

Ma question est simple: chaque fois que vous appelez cette fonction

test() 
test() 
test() 

en fait-il calculer k, et/ou cchaque fois que je l'appelle, ou bien sont-ils seulement calculées une fois ?

(si "seulement une fois", alors par curiosité: il fait cela la première fois que j'appelle la fonction? Ou peut-être le compilateur s'arrange-t-il pour le faire séparément au démarrage? Ou sait-il qu'il peut les calculer lors de la compilation?)


J'utilise souvent des propriétés globales calculées, un peu comme ce

let basicDF : DateFormatter = { 
    print("this will only be done once per launch of the app") 
    let formatter = DateFormatter() 
    formatter.dateFormat = "yyyy-MM-dd" 
    return formatter 
}() 

(peut-être avec fileprivate) Si la réponse à la question ci-dessus est « non c et « k 'sont calculés chaque fois que vous c tous les tests ", alors dans ce cas comment pouvez-vous mettre une sorte de propriété calculée statique à l'intérieur d'une fonction ??

Répondre

3

Non, dans votre cas particulier, le compilateur ne dispose actuellement pas d'optimiser c et/ou k expressions constantes qui ne sont évalués qu'une seule fois (ce qui peut être vu par examining the IR dans une version optimisée) - même si cela est soumis à changer avec les futures versions de la langue.

Cependant, il convient de noter qu'il peut actuellement le faire pour des expressions plus simples, par exemple:

func foo() -> Int { 
    return 2 + 4 
} 

Le compilateur peut évaluer l'ajout à la compilation de sorte que la fonction ne vient return 6 (et cela peut être inline). Mais bien sûr, vous ne devriez vous préoccuper de ces optimisations que si vous avez réellement identifié la fonction donnée comme un goulot d'étranglement.

Une astuce bien pour obtenir des constantes statiques dans une fonction est de définir un cas moins enum propriétés statiques dans le cadre de la fonction, dans laquelle vous pouvez définir vos expressions constantes:

func test() -> String { 

    enum Constants { 
     static let c = Array("abc".characters) 
     static let k = UInt32(c.count) 
    } 

    let r = Int(arc4random_uniform(Constants.k)) 
    return String(Constants.c[r]) 
} 

Maintenant, les deux l'initialiseur les expressions pour c et k ne seront évaluées qu'une seule fois, et cela sera fait lors de leur première utilisation (c'est-à-dire lorsque la fonction est appelée pour la première fois).

Et bien sûr, comme vous le montrer, vous pouvez utiliser une fermeture immédiatement-evalutated afin d'avoir une expression d'initialisation sur plusieurs lignes:

enum Constants { 
    static let c: [Character] = { 
     // ... 
     return Array("abc".characters) 
    }() 
    // ... 
} 
+0

Fantastique, c'est donc énum-statique de faire de la statique dans Swift. Exceptionnel, merci. Informations décisives du bitcode, merci. – Fattie

1

Je pense que vous devriez supposer que c et k sont calculés à chaque fois. Le calcul pourrait être optimisé en tant que détail d'implémentation du compilateur, mais je ne compterais pas dessus si j'étais vous. Swift n'a pas d'équivalent à une variable locale C static (c'est-à-dire une variable "automatique" dans une fonction dont la valeur est maintenue entre les appels à la fonction).

Si vous voulez vraiment vous assurer que k n'est calculé qu'une seule fois, faites-en une vraie constante (c'est-à-dire au niveau de la classe). Bien sûr, vous devrez également faire la même chose pour c, comme vous en avez besoin dans vos calculs ultérieurs. Il semble comme un exemple stupide dans ce cas, mais je le fais souvent quand la chose créée est lourd, comme une image ou une vue qui sera utilisé à plusieurs reprises:

class MyView : UIView { 
    lazy var arrow : UIImage = self.arrowImage() 
    func arrowImage() -> UIImage { 
     // ... big image-generating code goes here ... 
    } 
} 

j'appelle arrowImage() encore et jusqu'à ce que je me suis dit, attendez, je peux faire cela une constante (ici exprimée en lazy var) et le calculer une seule fois, à savoir la première fois arrow est accédé.

+0

Je vois, donc « var paresseux » est la solution à la niveau de classe ('paresseux var c = Array (" abc ".characters)', si simple); ne peut pas faire dans une fonction. Bonne info merci. – Fattie