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:
Por que Swift me forçou a colocar convenience
na frente do meu inicializador só porque eu preciso ligar self.init
dele? `
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:
class Foo {
var a: Int
}
a
é uma variável de instância não inicializada. Isso não será compilado a menos que forneça a
um valor padrão:
class Foo {
var a: Int = 0
}
ou inicialize a
em um método inicializador:
class Foo {
var a: Int
init(a: Int) {
self.a = a
}
}
Agora, vamos ver o que acontece se subclassificarmos Foo
, não é?
class Bar: Foo {
var b: Int
init(a: Int, b: Int) {
self.b = b
super.init(a: a)
}
}
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 que Bar
herdou Foo
o inicializador init(a: Int)
,. Mas isso não acontece. E como pôde? Como é Foo
do init(a: Int)
conhecimento como atribuir um valor para a b
variável que Bar
adicionou? Não faz. Portanto, não podemos inicializar uma Bar
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 :
Regra 1
Se sua subclasse não definir nenhum inicializador designado, ela herdará automaticamente todos os inicializadores designados da superclasse.
Regra 2
Se sua subclasse fornecer uma implementação de todos os inicializadores designados para sua superclasse - herdando-os de acordo com a regra 1 ou fornecendo uma implementação customizada como parte de sua definição -, ela herdará automaticamente todos os inicializadores de conveniência da superclasse.
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 :
class Base {
let a: Int
let b: Int
init(a: Int, b: Int) {
self.a = a
self.b = b
}
convenience init() {
self.init(a: 0, b: 0)
}
convenience init(a: Int) {
self.init(a: a, b: 0)
}
convenience init(b: Int) {
self.init(a: 0, b: b)
}
}
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.
class NonInheritor: Base {
let c: Int
init(a: Int, b: Int, c: Int) {
self.c = c
super.init(a: a, b: b)
}
}
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 do Base
classe está designado initializer: init(a: Int, b: Int)
. Isso significa que não herdamos nenhum inicializador de Base
:
Então, o que aconteceria se herdássemos Base
, mas seguimos em frente e implementamos um inicializador que correspondesse ao inicializador designado Base
?
class Inheritor: Base {
let c: Int
init(a: Int, b: Int, c: Int) {
self.c = c
super.init(a: a, b: b)
}
convenience override init(a: Int, b: Int) {
self.init(a: a, b: b, c: 0)
}
}
Agora, além dos dois inicializadores que implementamos diretamente nesta classe, porque implementamos um inicializador que corresponde Base
ao inicializador designado da classe, herdamos todos Base
os convenience
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 que Inheritor
possui apenas um inicializador designado. Portanto, se herdarmos de Inheritor
, teríamos que implementar esse inicializador designado e herdaríamos Inheritor
o inicializador de conveniência, o que, por sua vez, significa que implementamos todos Base
os inicializadores designados e podemos herdar seus convenience
inicializadores.