246 votes

Erreur dans la classe Swift : Propriété non initialisée à l'appel super.init

J'ai deux classes, Shape y Square

class Shape {
    var numberOfSides = 0
    var name: String
    init(name:String) {
        self.name = name
    }
    func simpleDescription() -> String {
        return "A shape with \(numberOfSides) sides."
    }
}

class Square: Shape {
    var sideLength: Double

    init(sideLength:Double, name:String) {
        super.init(name:name) // Error here
        self.sideLength = sideLength
        numberOfSides = 4
    }
    func area () -> Double {
        return sideLength * sideLength
    }
}

Avec l'implémentation ci-dessus, j'obtiens l'erreur suivante :

property 'self.sideLength' not initialized at super.init call
    super.init(name:name)

Pourquoi dois-je mettre self.sideLength avant d'appeler super.init ?

186voto

Ruben Points 1822

Citation tirée de The Swift Programming Language, qui répond à votre question :

"Le compilateur de Swift effectue quatre contrôles de sécurité utiles pour s'assurer que l'initialisation en deux phases se termine sans erreur :"

Contrôle de sécurité 1 "Un initialisateur désigné doit s'assurer que tous les fichiers " propriétés introduites par sa classe sont initialisées avant qu'il ne délègue à un initialisateur de superclasse".

Extrait de : Apple Inc. "The Swift Programming Language". iBooks. https://itunes.apple.com/us/book/swift-programming-language/id881256329?mt=11

53 votes

Il s'agit là d'un changement stupéfiant par rapport à C++, C# ou Java.

14 votes

@MDJ Bien sûr. Honnêtement, je n'en vois pas non plus la valeur ajoutée.

22 votes

En fait, il semble y en avoir un. Disons qu'en C#, le constructeur de la superclasse ne devrait pas appeler de méthodes (virtuelles) surchargées, car personne ne sait comment elles réagiraient avec une sous-classe non complètement initialisée. En Swift, tout va bien, car l'état de la sous-classe est correct lorsque le constructeur de la superclasse est en cours d'exécution. De plus, en Swift, toutes les méthodes sauf les méthodes finales sont surchargées.

120voto

Hitendra Hckr Points 302

Swift a une séquence très claire et spécifique d'opérations qui sont effectuées dans les initialisateurs. Commençons par quelques exemples de base pour arriver à un cas général.

Prenons un objet A. Nous allons le définir comme suit.

class A {
    var x: Int
    init(x: Int) {
        self.x = x
    }
}

Remarquez que A n'a pas de superclasse, il ne peut donc pas appeler une fonction super.init() puisqu'elle n'existe pas.

OK, alors maintenant, sous-classons A avec une nouvelle classe nommée B.

class B: A {
    var y: Int
    init(x: Int, y: Int) {
        self.y = y
        super.init(x: x)
    }
}

C'est une différence par rapport à l'Objective-C où [super init] est généralement appelé en premier avant toute autre chose. Ce n'est pas le cas en Swift. Vous devez vous assurer que vos variables d'instance sont dans un état cohérent avant de faire quoi que ce soit d'autre, y compris appeler des méthodes (ce qui inclut l'initialisateur de votre superclasse).

2 votes

C'est extrêmement utile, un exemple clair. merci !

0 votes

Et si j'ai besoin de la valeur pour calculer y, par exemple : init(y : Int) { self.y = y * self.x super.init() }

1 votes

Utilisez quelque chose comme, init(y : Int, x : Int = 0) { self.y = y * x ; self.x = x ; super.init(x : x) } Vous ne pouvez pas non plus appeler directement le constructeur vide de la super classe en vous référant à l'exemple ci-dessus, car la super classe A n'a pas de constructeur vide.

38voto

Shuyang Sun Points 31

La fonction "super.init()" doit être appelée après avoir initialisé toutes vos variables d'instance.

Dans la vidéo "Intermediate Swift" d'Apple (vous pouvez la trouver sur la page des ressources vidéo pour développeurs d'Apple). https://developer.apple.com/videos/wwdc/2014/ ), à environ 28:40, il est explicitement dit que tous les initialisateurs de la super classe doivent être appelés APRÈS que vous ayez initialisé vos variables d'instance.

En Objective-C, c'était l'inverse. En Swift, puisque toutes les propriétés doivent être initialisées avant d'être utilisées, nous devons d'abord initialiser les propriétés. Ceci a pour but d'empêcher un appel à une fonction surchargée par la méthode "init()" de la super classe, sans initialiser les propriétés en premier.

Donc l'implémentation de "Square" devrait être :

class Square: Shape {
    var sideLength: Double

    init(sideLength:Double, name:String) {
        self.sideLength = sideLength
        numberOfSides = 4
        super.init(name:name) // Correct position for "super.init()"
    }
    func area () -> Double {
        return sideLength * sideLength
    }
}

1 votes

Je n'aurais jamais deviné. L'initialisation avec super devrait être une première déclaration, c'est ce que j'aurais pensé. ! ! hm. un sacré changement avec swift.

0 votes

Pourquoi cela doit-il venir après ? Veuillez fournir une raison technique

16voto

fnc12 Points 191

Désolé pour l'horrible formatage. Mettez simplement un caractère d'interrogation après la déclaration et tout ira bien. Une question indique au compilateur que la valeur est facultative.

class Square: Shape {
    var sideLength: Double?   // <=== like this ..

    init(sideLength:Double, name:String) {
        super.init(name:name) // Error here
        self.sideLength = sideLength
        numberOfSides = 4
    }
    func area () -> Double {
        return sideLength * sideLength
    }
}

Edit1 :

Il existe un meilleur moyen d'éviter cette erreur. Selon le commentaire de jmaschad, il n'y a aucune raison d'utiliser optional dans votre cas, car les optionals ne sont pas confortables à utiliser et vous devez toujours vérifier si optional n'est pas nil avant d'y accéder. Donc tout ce que vous avez à faire est d'initialiser le membre après la déclaration :

class Square: Shape {
    var sideLength: Double=Double()   

    init(sideLength:Double, name:String) {
        super.init(name:name)
        self.sideLength = sideLength
        numberOfSides = 4
    }
    func area () -> Double {
        return sideLength * sideLength
    }
}

Edit2 :

Après avoir obtenu deux "moins" pour cette réponse, j'ai trouvé un moyen encore meilleur. Si vous voulez que le membre de la classe soit initialisé dans votre constructeur, vous devez lui assigner une valeur initiale à l'intérieur du constructeur et avant l'appel à super.init(). Comme ceci :

class Square: Shape {
    var sideLength: Double  

    init(sideLength:Double, name:String) {
        self.sideLength = sideLength   // <= before super.init call..
        super.init(name:name)
        numberOfSides = 4
    }
    func area () -> Double {
        return sideLength * sideLength
    }
}

Bonne chance dans l'apprentissage de Swift.

0 votes

Il suffit de changer super.init(name:name) y self.sideLength = sideLength . Déclarer sideLength comme optionnel est une erreur et introduit des tracas supplémentaires plus tard lorsque vous devez le déballer de force.

0 votes

Oui, c'est une option. Merci

0 votes

Vous pouvez en fait juste avoir var sideLength: Double il n'est pas nécessaire de lui attribuer une valeur initiale

9voto

Daij-Djan Points 18696

Swift vous oblige à initialiser chaque variable membre avant qu'elle ne soit ou ne puisse être utilisée. Comme il ne peut pas être sûr de ce qui se passera quand ce sera le tour de supers, il se trompe : mieux vaut prévenir que guérir.

1 votes

Cela n'a pas de sens, car la classe mère ne devrait pas avoir de visibilité sur les propriétés déclarées dans ses enfants !

1 votes

Ce n'est pas le cas, mais vous pouvez remplacer des choses et commencer à utiliser le self "avant que super ne le fasse".

0 votes

Pouvez-vous voir aquí et les commentaires qui le suivent ? Je pense que je dis exactement ce que vous dites, c'est-à-dire que nous disons tous les deux que le compilateur veut être sûr plutôt que désolé, ma seule question est, alors comment objective-c a résolu ce problème ? Ou pas ? Si ce n'est pas le cas, alors pourquoi faut-il toujours que vous écriviez super.init dans la première ligne ?

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