[Swift 3-4] override lazy var ?

Bonjour à  tous !  :)


 


J'ai fait des tests hier soir sur playground et visiblement on ne peut pas surcharger une propriété lazy. Exemple :



class A {
lazy foo: CGFloat = {
return CGFloat(2.0)
}()
init(){ }
}

class B: A {
// Ne compile pas
override lazy foo: CGFloat = {
return CGFloat(3.0)
}()
init(){ }
}

Pour contourner le problème j'ai pensé à  quelque chose comme ça :



class A {
lazy foo: CGFloat = {
return self.getFoo()
}()
init(){ }
func getFoo()-> CGFloat {
return CGFloat(2.0)
}
}

class B: A {
init(){ }
override func getFoo()-> CGFloat {
return CGFloat(3.0)
}
}

Mais je ne trouve pas cette solution très élégante...  -_-


 


Quelqu'un aurait il mieux à  proposer ? Bien entendu le lazy n'a aucun intérêt dans le cas ci-dessus, ce qui m'intéresse c'est le principe de fonctionnement.


 


Je suis ouverte à  toute proposition (décente, bien entendu).  :P


Mots clés:

Réponses

  • Joanna CarterJoanna Carter Membre, Modérateur
    août 2017 modifié #2

    Ton code ne compile pas du tout. il manque le moat "var".


     


    Quand même, en le corrigeant, t'as raison que l'on ne peux pas overrider les lazy vars.


     


    C'est pareil avec les vars qui ne sont pas lazy mais que l'on veuille initialiser dans la déclaration :



    class A
    {
    var foo: CGFloat = 2.0

    init(){ }
    }

    class B: A
    {
    override var foo: CGFloat = 3.0 // error : Cannot override with a stored property 'foo'

    override init(){ super.init() }
    }

    Les lazy vars ne sont que les stored vars qui sont initialisées sur le premier appel ; du coup, même principe.


     


    La solution : n'utilise, ni les lazy vars, ni les vars initialisées si tu prévois les overrider.


     


    Pour le comportement que tu recherches, il faut utiliser les "computed" vars :



    class A
    {
    var foo: CGFloat
    {
    return 2.0
    }

    init(){ }
    }

    class B: A
    {
    override var foo: CGFloat
    {
    return 3.0
    }

    override init(){ super.init() }
    }

    Ou, si tu prévois changer la valeur après l'initialisation, tu peux faire :



    class A
    {
    var foo: CGFloat

    init(foo: CGFloat)
    {
    self.foo = foo
    }

    convenience init()
    {
    self.init(foo: 2.0)
    }
    }

    class B: A
    {
    init()
    {
    super.init(foo: 3.0)
    }
    }


  • Ton code ne compile pas du tout. il manque le mot "var".




     


    J'ai créer ce bout de code au moment où j'ai ouvert le post. J'avais rien pour tester si ça fonctionnait.  o:)


     


    Ouai c'est bien chiant...  ::)


     


    J'avais bien pensé à  ce cas là  mais le souci c'est qu'à  chaque appel il va recréer une instance de type CGFloat. Chaque appel de la property foo retournera un nouvel objet. J'ai juste ? Si c'est la cas ça ne me va pas...  :)


     


    Sinon la solution que j'ai proposée avec une surcharge de fonction, tu valides ?


  • Joanna CarterJoanna Carter Membre, Modérateur

    Tu n'as pas encore dit si tu prévois changer la valeur après l'initialisation. La réponse à  ta question dépend sur ce condition.


     


    Dans le cas d'un CGFloat, on ne crée pas un objet ; CGFloat est un struct qui à  tout modification sera copié du coup, si tu faisais :



    {
    var a = A()

    let f = a.foo

    ...
    }

    ... le let f contiendra a.foo jusqu'au moment ou tu le modifie ; là  il sera copié. ça c'est la nature des structs.


  • JérémyJérémy Membre
    août 2017 modifié #5


    Tu n'as pas encore dit si tu prévois changer la valeur après l'initialisation. 




     


    Oui effectivement...  :P


     


    En fait non, elle doit garder la valeur de la première initialisation (comme un lazy) et de mon côté ce seront des classes qui seront retournées et non des structures. ;)


  • Joanna CarterJoanna Carter Membre, Modérateur
    août 2017 modifié #6

    S'il y a une possibilité que les objets dans les vars ne soit pas utilisé, tu avais raison de préférer les lazy vars.


     


    Du coup, une lazy var qui appelle une méthode virtuelle sera la meilleur solution :



    class BigThing
    {
    let value: String

    init(value: String)
    {
    self.value = value
    }
    }

    class A
    {
    func createBigThing() -> BigThing
    {
    return BigThing(value: "BigThing A")
    }

    lazy var foo: BigThing =
    {
    return self.createBigThing()
    }()
    }

    class B: A
    {
    override func createBigThing() -> BigThing
    {
    return BigThing(value: "BigThing B")
    }
    }

    createBigThing() ne sera appeler qu'une fois, lors du premier appel au lazy var. Après ça, le lazy var gardera le même objet.


  • ça marche !   Merci beaucoup !  :)


     


    Vraiment pénible qu'on ne puisse pas overrider un lazy (du moins une propriété stockée). Je vois pas trop où serait le problème...  :o

  • Joanna CarterJoanna Carter Membre, Modérateur
    août 2017 modifié #8

    Les lazy vars sont, en substance, l'équivalent de qqch comme :



    class A
    {
    func createBigThing() -> BigThing
    {
    return BigThing(value: "BigThing A")
    }

    var foo: BigThing = createBigThing()
    }

    Mais, avec ça, on a l'erreur : Cannot use instance member 'createBigThing' within property initializer; property initializers run before 'self' is available


     


    Du coup, c'est pour les raisons comme ça que l'on a inventé les lazy vars, qui sont, en substance, une var qui est initialisée par un closure, à  la place d'une var qui ne peux pas être initialisée par une méthode.


     


    En fait, on peut oublier le closure sur le lazy var en faisant :



    class A
    {
    func createBigThing() -> BigThing
    {
    return BigThing(value: "BigThing A")
    }

    lazy var foo: BigThing = self.createBigThing()
    }

    ... parce que, en utilisant "lazy", ça dit au compilateur que la var ne puisse pas être appelé avant que "self" soit disponible.




  • ... parce que, en utilisant "lazy", ça dit au compilateur que la var ne puisse pas être appelé avant que "self" soit disponible.




     


    Oui c'est exactement ça. Pout être plus précis, qu'il n'est pas obligatoire que la propriété soit instanciée à  l'issue de l'instanciation de la classe qui la contient. En d'autres termes, elle ne doit pas être forcément initialisé avant ou à  l'issue de l'appel de la méthode init. D'où le fait (comme tu le dis) que l'on utilise généralement cette mécanique pour construire des propriétés stockées après que "self" ait été construit.


     




    Je vois pas trop où serait le problème...  :o




     


    Tu vois pas où est le problème ? Bah c'est pourtant simple. Toi tu résonnes comme si ton lazy var était une méthode. Effectivement dans le cadre d'une fonction tu peux la surcharger dans les classes filles. Alors que dans ton cas, même si tu as une closure qui te donne l'impression d'écrire une méthode, ton lazy n'est rien d'autre qu'une propriété stockée où l'instanciation de la classe qui la contient peut précéder son initialisation.  ;)


     


    ça te viendrait à  l'esprit de surcharger une propriété protected ou public ( :o ) d'une classe en Java ?  :D

  • Joanna CarterJoanna Carter Membre, Modérateur


     




    Je vois pas trop où serait le problème...  :o




     


    Tu vois pas où est le problème ?




     


    Non, c'est toi qui l'a dit ; c'est toi qui a cité toi-même. Mais quand-même   ::)


     




    Toi tu résonnes comme si ton lazy var était une méthode. Effectivement dans le cadre d'une fonction tu peux la surcharger dans les classes filles. Alors que dans ton cas, même si tu as une closure qui te donne l'impression d'écrire une méthode, ton lazy n'est rien d'autre qu'une propriété stockée où l'instanciation de la classe qui la contient peut précéder son initialisation.  ;)




     


    Je n'ai jamais parlé d'un lazy var comme il était une méthode ; il est une propriété stockée, initialisée par une méthode (qui est une type de closure), et on peut overrider la méthode d'initialisation comme toutes autres méthodes. Mais on ne peut pas overrider le var lui-même.


     


    Je ne comprends pas vraiment ce que tu veux dire. Un lazy var a été conçu exprès pour qu'il ne faille l'instancier qu'au premier appel. C'est l'équivalent de ce que l'on faisait auparavant :



    class A
    {
    var _foo: BigThing?

    var foo: BigThing
    {
    if _foo == nil
    {
    _foo = BigThing(value: "A")
    }

    return _foo!
    }



    ça te viendrait à  l'esprit de surcharger une propriété protected ou public ( :o ) d'une classe en Java ?  :D




     


     Je ne touche jamais Java ; j'en suis allergique  :-*



  •  


     Je ne touche jamais Java ; j'en suis allergique  :-*




  • JérémyJérémy Membre
    août 2017 modifié #12


    Non, c'est toi qui l'a dit ; c'est toi qui a cité toi-même. Mais quand-même   ::)




     


    Oui je sais, j'avais trouvé la réponse et je me répondais à  moi même pour ceux qui seraient intéressés d'avoir une explication.  ;)


     




    Je ne touche jamais Java ; j'en suis allergique  :-*




     


    Normal, c'est super poussiéreux comme truc. Même un Dyson n'y pourra rien. Quand tu passes de Swift à  Java tu as la sensation de faire un bon dans le moyen âge.  :D


Connectez-vous ou Inscrivez-vous pour répondre.