protocol Car {
var wheels : Int { get set}
init(wheels: Int)
}
extension Car {
init(wheels: Int) {
self.wheels = wheels
}
}
sur self.wheels = roues, je reçois l'erreur
Error: variable 'self' passed by reference before being initialized
Comment puis-je définir l'initialiseur dans l'extension de protocole?
Comme vous pouvez le voir, cela ne fonctionne pas dans ces circonstances car lors de la compilation, vous devez vous assurer que toutes les propriétés sont initialisées avant d'utiliser la structure/enum/class.
Vous pouvez rendre un autre initialiseur obligatoire afin que le compilateur sache que toutes les propriétés sont initialisées:
protocol Car {
var wheels : Int { get set }
// make another initializer
// (which you probably don't want to provide a default implementation)
// a protocol requirement. Care about recursive initializer calls :)
init()
init(wheels: Int)
}
extension Car {
// now you can provide a default implementation
init(wheels: Int) {
self.init()
self.wheels = wheels
}
}
// example usage
// mark as final
final class HoverCar: Car {
var wheels = 0
init() {}
}
let drivableHoverCar = HoverCar(wheels: 4)
drivableHoverCar.wheels // 4
Depuis Xcode 7.3 beta 1, il fonctionne avec structs
comme prévu mais pas avec les classes car si elles ne sont pas final
la init(wheels: Int)
dans le protocole est un required init
et il peut être remplacé, il ne peut donc pas être ajouté via une extension. Solution de contournement (comme le suggère le complice): créez le class
final
.
final class
)Pour travailler avec des classes sans les rendre définitives, vous pouvez également supprimer l'exigence init(wheels: Int)
dans le protocole. Il semble qu'il ne se comporte pas différemment qu'auparavant, mais considérez ce code:
protocol Car {
var wheels : Int { get set }
init()
// there is no init(wheels: Int)
}
extension Car {
init(wheels: Int) {
self.init()
print("Extension")
self.wheels = wheels
}
}
class HoverCar: Car {
var wheels = 0
required init() {}
init(wheels: Int) {
print("HoverCar")
self.wheels = wheels
}
}
// prints "HoverCar"
let drivableHoverCar = HoverCar(wheels: 4)
func makeNewCarFromCar<T: Car>(car: T) -> T {
return T(wheels: car.wheels)
}
// prints "Extension"
makeNewCarFromCar(drivableHoverCar)
Donc, si vous créez un Car
à partir d'un contexte générique où le type sur lequel vous appelez init
ne doit être connu que comme Car
l'initialiseur d'extension est appelé même si un initialiseur est défini dans HoverCar
. Cela se produit uniquement car il n'y a pas d'exigence init(wheels: Int)
dans le protocole.
Si vous l'ajoutez, vous avez l'ancien problème avec la déclaration de class
comme final
mais maintenant il imprime deux fois "HoverCar". Quoi qu'il en soit, le deuxième problème ne se produit probablement jamais, il pourrait donc être une meilleure solution.
Sidenote: Si j'ai fait quelques erreurs (code, langue, grammaire, ...) vous êtes les bienvenus pour me corriger :)
@Qbyte est correct.
De plus, vous pouvez jeter un oeil à mon Configurable
En ce que j'ai le protocole Initable
public protocol Initable {
// To make init in protocol extension work
init()
}
public extension Initable {
public init(@noescape block: Self -> Void) {
self.init()
block(self)
}
}
Ensuite, pour s'y conformer
extension Robot: Initable { }
J'ai 2 façons, en utilisant final
ou implémenter init
final class Robot {
var name: String?
var cute = false
}
class Robot {
var name: String?
var cute = false
required init() {
}
}
Ma compréhension est que cela n'est pas possible, car l'extension de protocole ne peut pas savoir quelles propriétés possède la classe ou la structure conforme - et ne peut donc pas garantir qu'elles sont correctement initialisées.
S'il existe des moyens de contourner cela, je suis très intéressé de savoir! :)
Peut ne pas être le même mais dans mon cas, au lieu d'utiliser init, j'ai utilisé un func statique pour renvoyer l'objet de la classe.
protocol Serializable {
static func object(fromJSON json:JSON) -> AnyObject?
}
class User {
let name:String
init(name:String) {
self.name = name
}
}
extension User:Serializable {
static func object(fromJSON json:JSON) -> AnyObject? {
guard let name = json["name"] else {
return nil
}
return User(name:name)
}
}
Ensuite, pour créer l'objet, je fais quelque chose comme:
let user = User.object(fromJSON:json) as? User
Je sais que ce n'est pas la meilleure chose qui soit, mais c'est la meilleure solution que j'ai pu trouver pour ne pas coupler le modèle commercial avec la couche de données.
REMARQUE: je suis paresseux et j'ai tout codé directement dans le commentaire, donc si quelque chose ne fonctionne pas, faites le moi savoir.