Como o Swift oferece suporte à sobrecarga de método e inicializador, você pode colocar múltiplos init
ao lado do outro e usar o que achar conveniente:
class Person {
var name:String
init(name: String) {
self.name = name
}
init() {
self.name = "John"
}
}
Então, por que a convenience
palavra-chave existiria? O que torna o seguinte substancialmente melhor?
class Person {
var name:String
init(name: String) {
self.name = name
}
convenience init() {
self.init(name: "John")
}
}
swift
initialization
Desmond Hume
fonte
fonte
Respostas:
As respostas existentes contam apenas metade da
convenience
história. A outra metade da história, a metade que nenhuma das respostas existentes cobre, responde à pergunta que Desmond postou nos comentários:Eu o toquei levemente nesta resposta , na qual abordo várias regras de inicializador de Swift em detalhes, mas o foco principal estava na
required
palavra. Mas essa resposta ainda estava abordando algo que é relevante para esta pergunta e esta resposta. Temos que entender como a herança do inicializador Swift funciona.Como o Swift não permite variáveis não inicializadas, não é garantido que você herda todos (ou quaisquer) inicializadores da classe que você herda. Se subclassificarmos e adicionarmos variáveis de instância não inicializadas a nossa subclasse, paramos de herdar inicializadores. E até adicionarmos os inicializadores, o compilador gritará conosco.
Para ser claro, uma variável de instância não inicializada é qualquer variável de instância que não recebe um valor padrão (tendo em mente que os opcionais e opcionais implicitamente desembrulhados assumem automaticamente um valor padrão de
nil
).Então, neste caso:
a
é uma variável de instância não inicializada. Isso não será compilado a menos que forneçaa
um valor padrão:ou inicialize
a
em um método inicializador:Agora, vamos ver o que acontece se subclassificarmos
Foo
, não é?Certo? Adicionamos uma variável e um inicializador para definir um valor para
b
que ele seja compilado. Dependendo do idioma de origem, você pode esperar queBar
herdouFoo
o inicializadorinit(a: Int)
,. Mas isso não acontece. E como pôde? Como éFoo
doinit(a: Int)
conhecimento como atribuir um valor para ab
variável queBar
adicionou? Não faz. Portanto, não podemos inicializar umaBar
instância com um inicializador que não possa inicializar todos os nossos valores.O que isso tem a ver
convenience
?Bem, vejamos as regras sobre herança de inicializador :
Observe a Regra 2, que menciona os inicializadores de conveniência.
Portanto, o que a
convenience
palavra - chave faz é indicar quais inicializadores podem ser herdados por subclasses que adicionam variáveis de instância sem valores padrão.Vamos dar uma
Base
aula de exemplo :Observe que temos três
convenience
inicializadores aqui. Isso significa que temos três inicializadores que podem ser herdados. E temos um inicializador designado (um inicializador designado é simplesmente qualquer inicializador que não seja um inicializador de conveniência).Podemos instanciar instâncias da classe base de quatro maneiras diferentes:
Então, vamos criar uma subclasse.
Nós estamos herdando de
Base
. Adicionamos nossa própria variável de instância e não atribuímos um valor padrão, portanto, devemos adicionar nossos próprios inicializadores. Nós adicionamos um,init(a: Int, b: Int, c: Int)
mas isso não corresponde à assinatura doBase
classe está designado initializer:init(a: Int, b: Int)
. Isso significa que não herdamos nenhum inicializador deBase
:Então, o que aconteceria se herdássemos
Base
, mas seguimos em frente e implementamos um inicializador que correspondesse ao inicializador designadoBase
?Agora, além dos dois inicializadores que implementamos diretamente nesta classe, porque implementamos um inicializador que corresponde
Base
ao inicializador designado da classe, herdamos todosBase
osconvenience
inicializadores da classe :O fato de o inicializador com a assinatura correspondente ser marcado como
convenience
não faz diferença aqui. Isso significa apenas queInheritor
possui apenas um inicializador designado. Portanto, se herdarmos deInheritor
, teríamos que implementar esse inicializador designado e herdaríamosInheritor
o inicializador de conveniência, o que, por sua vez, significa que implementamos todosBase
os inicializadores designados e podemos herdar seusconvenience
inicializadores.fonte
init(a: Int)
deixariab
não inicializado.Principalmente clareza. Do seu segundo exemplo,
é obrigatório ou designado . Ele precisa inicializar todas as suas constantes e variáveis. Os inicializadores de conveniência são opcionais e geralmente podem ser usados para facilitar a inicialização. Por exemplo, digamos que sua classe Person tenha uma variável opcional gender:
onde Gender é um enum
você poderia ter inicializadores de conveniência como este
Os inicializadores de conveniência devem chamar os inicializadores designados ou necessários neles. Se sua classe é uma subclasse, ela deve chamar
super.init()
dentro da inicialização.fonte
convenience
palavra-chave, mas o Swift ainda estaria incomodando. Esse não é o tipo de simplicidade eu estava esperando da Apple =)Bem, a primeira coisa que me vem à mente é que ela é usada na herança de classe para organização e legibilidade do código. Continuando com sua
Person
turma, pense em um cenário como esteCom o inicializador de conveniência, sou capaz de criar um
Employee()
objeto sem valor, daí a palavraconvenience
fonte
convenience
palavras-chave removidas, o Swift não obteria informações suficientes para se comportar da mesma maneira exata?convenience
palavra - chave, não poderá inicializar oEmployee
objeto sem nenhum argumento.Employee()
chama oconvenience
inicializador (herdado, devido a )init()
, que chamaself.init(name: "Unknown")
.init(name: String)
, também um inicializador de conveniência paraEmployee
, chama o inicializador designado.Além dos pontos que outros usuários explicaram aqui, está o meu entendimento.
Sinto fortemente a conexão entre o inicializador de conveniência e as extensões. Quanto a mim, os inicializadores de conveniência são mais úteis quando desejo modificar (na maioria dos casos, torná-lo curto ou fácil) a inicialização de uma classe existente.
Por exemplo, alguma classe de terceiros que você usa possui
init
quatro parâmetros, mas no seu aplicativo os dois últimos têm o mesmo valor. Para evitar mais digitação e limpar seu código, você pode definir umconvenience init
com apenas dois parâmetros e, dentro dele, chamarself.init
com último para parâmetros com valores padrão.fonte
convenience
na frente do meu inicializador só porque eu preciso ligarself.init
dele? Isso parece redundante e meio inconveniente.De acordo com a documentação do Swift 2.1 , os
convenience
inicializadores precisam seguir algumas regras específicas:Um
convenience
inicializador só pode chamar intializers na mesma classe, não em superclasses (somente entre, não para cima)Um
convenience
inicializador deve chamar um inicializador designado em algum lugar da cadeiaUm
convenience
inicializador não pode alterar QUALQUER propriedade antes de chamar outro inicializador - enquanto um inicializador designado precisa inicializar propriedades introduzidas pela classe atual antes de chamar outro inicializador.Ao usar a
convenience
palavra - chave, o compilador Swift sabe que precisa verificar essas condições - caso contrário, não poderia.fonte
convenience
palavra - chave.let
propriedades). Não pode inicializar propriedades. Um inicializador designado tem a responsabilidade de inicializar todas as propriedades introduzidas antes de chamar umsuper
inicializador designado.Uma classe pode ter mais de um inicializador designado. Um inicializador de conveniência é um inicializador secundário que deve chamar um inicializador designado da mesma classe.
fonte