Eu tenho duas aulas Shape
eSquare
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
}
}
Com a implementação acima, recebo o erro:
property 'self.sideLength' not initialized at super.init call
super.init(name:name)
Por que eu tenho que definir self.sideLength
antes de ligar super.init
?
properties
compiler-errors
swift
JuJoDi
fonte
fonte
Respostas:
Cite a linguagem de programação Swift, que responde sua pergunta:
fonte
init
.O Swift tem uma sequência muito clara e específica de operações que são feitas nos inicializadores. Vamos começar com alguns exemplos básicos e avançar até um caso geral.
Vamos pegar um objeto A. Vamos defini-lo da seguinte forma.
Observe que A não possui uma superclasse, portanto, não pode chamar uma função super.init () porque ela não existe.
OK, agora vamos subclassificar A com uma nova classe chamada B.
Este é um desvio do Objective-C, onde
[super init]
normalmente seria chamado primeiro antes de qualquer outra coisa. Não é assim em Swift. Você é responsável por garantir que suas variáveis de instância estejam em um estado consistente antes de fazer qualquer outra coisa, incluindo métodos de chamada (que incluem o inicializador da sua superclasse).fonte
Dos documentos
Por que precisamos de uma verificação de segurança como esta?
Para responder a isso, vamos rapidamente ao processo de inicialização.
Portanto, para garantir que o processo de inicialização em duas etapas seja realizado conforme definido acima, há quatro verificações de segurança, uma delas é,
Agora, a inicialização em duas fases nunca fala sobre ordem, mas essa verificação de segurança é introduzida
super.init
após a inicialização de todas as propriedades.A verificação de segurança 1 pode parecer irrelevante, pois a inicialização em duas fases impede que os valores das propriedades sejam acessados antes de serem inicializados , sem essa verificação de segurança 1.
Como nesta amostra
Triangle.init
foi inicializado, todas as propriedades antes de serem usadas. Portanto, a verificação de segurança 1 parece irrelevante,Mas então pode haver outro cenário, um pouco complexo,
Resultado :
Aqui, se tivéssemos chamado o
super.init
antes de definir ohypotenuse
, asuper.init
chamada então chamaria oprintShapeDescription()
e, uma vez que isso foi substituído, ele retornaria primeiro à implementação da classe Triangle deprintShapeDescription()
. AprintShapeDescription()
classe of Triangle acessahypotenuse
uma propriedade não opcional que ainda não foi inicializada. E isso não é permitido, pois a inicialização em duas fases impede que os valores de propriedade sejam acessados antes de serem inicializados.Portanto, verifique se a inicialização em duas fases é feita conforme definido, precisa haver uma ordem específica de chamada
super.init
, ou seja, depois de inicializar todas as propriedades introduzidas pelaself
classe, precisamos de uma verificação de segurança 1fonte
init
pode chamar uma função (substituída) ... na qual essa função acessa a propriedade subclasses, para evitar não ter valores definidos, a chamadasuper
deve ocorrer após todos os valores serem definidos. OK faz sentido. Você quer saber como a Objective-C fez isso na época e por que você teve que ligarsuper
primeiro?printShapeDescription()
antesself.sides = sides; self.name = named;
que geraria este erro:use of 'self' in method call 'printShapeDescription' before all stored properties are initialized
. O erro do OP é fornecido para reduzir a 'possibilidade' de erro de tempo de execução.printShapeDescription
fosse uma função que não se referisse,self
isto é, algo como `print (" nothing "), então não haveria problemas. (No entanto, mesmo para que o compilador iria lançar um erro, porque não é super- inteligente)O "super.init ()" deve ser chamado depois que você inicializar todas as suas variáveis de instância.
No vídeo "Intermediate Swift" da Apple (você pode encontrá-lo na página de recursos de vídeo do desenvolvedor Apple https://developer.apple.com/videos/wwdc/2014/ ), por volta das 28:40, é explícito que todos os inicializadores em superclasse deve ser chamado DEPOIS de inicializar suas variáveis de instância.
No Objetivo-C, foi o contrário. No Swift, como todas as propriedades precisam ser inicializadas antes de serem usadas, precisamos inicializar as propriedades primeiro. Isso visa impedir que uma chamada para a função substituída seja do método "init ()" da superclasse, sem inicializar as propriedades primeiro.
Portanto, a implementação do "Square" deve ser:
fonte
Desculpe pela formatação feia. Basta colocar um caractere de pergunta após a declaração e tudo ficará bem. Uma pergunta informa ao compilador que o valor é opcional.
Edit1:
Existe uma maneira melhor de ignorar esse erro. De acordo com o comentário do jmaschad, não há razão para usar o opcional no seu caso, pois os opcionais não são confortáveis no uso e você sempre deve verificar se o opcional não é nulo antes de acessá-lo. Então, tudo que você precisa fazer é inicializar o membro após a declaração:
Edit2:
Depois de dois pontos negativos nessa resposta, encontrei um caminho ainda melhor. Se você deseja que um membro da classe seja inicializado em seu construtor, deve atribuir um valor inicial a ele dentro do contratante e antes da chamada super.init (). Como isso:
Boa sorte em aprender Swift.
fonte
super.init(name:name)
eself.sideLength = sideLength
. DeclararsideLength
como opcional é enganoso e apresenta problemas adicionais mais tarde, quando você precisa forçar o desembrulhamento.var sideLength: Double
, não há necessidade de atribuir-lhe um valor inicialO swift obriga a inicializar cada var de membro antes que ele seja usado. Uma vez que não se pode ter certeza do que acontece quando é a hora da virada, ocorre um erro: é melhor prevenir do que remediar
fonte
super.init
na primeira linha?Edward,
Você pode modificar o código no seu exemplo assim:
Isso está usando um opcional implicitamente desembrulhado.
Na documentação, podemos ler:
fonte
Swift não permitirá que você inicialize superclasse sem inicializar as propriedades, ao contrário do Obj C. Portanto, você deve inicializar todas as propriedades antes de chamar "super.init".
Acesse http://blog.scottlogic.com/2014/11/20/swift-initialisation.html . Dá uma boa explicação para o seu problema.
fonte
Adicione zero ao final da declaração.
Isso funcionou para o meu caso, mas pode não funcionar para o seu
fonte
Você está apenas começando na ordem errada.
fonte
Provavelmente vou receber alguns votos negativos, mas, para ser sincero, a vida é mais fácil assim:
fonte
É um design incrivelmente estúpido.
Considere algo como isto:
Isso é inválido, conforme observado acima. Mas assim é:
Porque 'self' não foi inicializado.
Espero sinceramente que esse bug seja corrigido em breve.
(Sim, eu sei que eu poderia criar um objeto vazio e depois definir o tamanho, mas isso é estúpido).
fonte