Considere as duas classes:
class A {
var x: Int
init(x: Int) {
self.x = x
}
convenience init() {
self.init(x: 0)
}
}
class B: A {
init() {
super.init() // Error: Must call a designated initializer of the superclass 'A'
}
}
Não vejo por que isso não é permitido. No final das contas, o inicializador designado de cada classe é chamado com quaisquer valores de que precisam, então por que preciso me repetir em B
's init
especificando um valor padrão para x
novamente, quando a conveniência init
em A
bastará?
class
initialization
swift
Robert
fonte
fonte
Respostas:
Esta é a Regra 1 das regras de "Encadeamento de inicializador" conforme especificado no Guia de programação Swift, que diz:
https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Initialization.html
Ênfase minha. Os inicializadores designados não podem chamar inicializadores de conveniência.
Há um diagrama que acompanha as regras para demonstrar quais "direções" do inicializador são permitidas:
fonte
SCNGeometry
: você só pode adicionarSCNGeometryElement
s usando o inicializador de conveniência, portanto, não se pode herdar dele.Considerar
Como todos os inicializadores designados da classe A são substituídos, a classe B herdará todos os inicializadores de conveniência de A. Portanto, a execução disso resultará
Agora, se o inicializador designado B.init (a: b :) tivesse permissão para chamar o inicializador de conveniência da classe base A.init (a :), isso resultaria em uma chamada recursiva para B.init (a:, b: )
fonte
É porque você pode acabar com uma recursão infinita. Considerar:
e olhe para:
que chamará
SubClass.init()
que ligaráSuperClass.init(value:)
que ligaráSubClass.init()
.As regras de inicialização designadas / de conveniência são projetadas de forma que a inicialização de uma classe esteja sempre correta.
fonte
Eu encontrei uma solução alternativa para isso. Não é muito bonito, mas resolve o problema de não saber os valores de uma superclasse ou de querer definir os valores padrão.
Tudo o que você precisa fazer é criar uma instância da superclasse, usando a conveniência
init
, diretamenteinit
da subclasse. Em seguida, você chama o designadoinit
do super usando a instância que acabou de criar.fonte
Considere extrair o código de inicialização de sua função conveniente
init()
para uma nova função auxiliarfoo()
, chamadafoo(...)
para fazer a inicialização em sua subclasse.fonte
Veja o vídeo WWDC "403 intermediário Swift" às 18:30 para uma explicação detalhada dos inicializadores e sua herança. Pelo que entendi, considere o seguinte:
Mas agora considere uma subclasse da Wyrm: Uma Wyrm é um Dragão sem pernas e sem asas. Portanto, o inicializador para um Wyvern (2 pernas, 2 asas) é errado para isso! Esse erro pode ser evitado se o Wyvern-Initializer de conveniência simplesmente não puder ser chamado, mas apenas o Initializer completo designado:
fonte
initWyvern
fizer sentido ser chamado?Wyrm
substituição do número de trechos após chamar um inicializador de conveniência.Por que você não tem apenas dois inicializadores - um com um valor padrão?
fonte