O que exatamente é init coder aDecoder?

122

Estou aprendendo o desenvolvimento para iOS em um curso online e sempre que faço uma visualização personalizada (célula de visualização de tabela customizada, célula de visualização de coleção, etc.) o instrutor sempre implementa este inicializador:

required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
}

Por que exatamente eu sempre tenho que ligar assim? O que isso faz? Posso colocar propriedades dentro do init?

JasonP
fonte
5
Esta resposta o ajudará a stackoverflow.com/questions/24036393/… Obrigado
Seungyoun Yi
2
Se você criar uma subclasse de um objeto que implementa NSCoding, será necessário implementar esse inicializador, já que ele é necessário para as classes que o implementam NSCoding. Você deve pelo menos chamar o método init da superclasse. Se o NSCodercontém propriedades codificadas para sua classe, você pode usar este método para recuperá-las
Paulw11
1
Além disso, recomendo que você leia a seção sobre a inicialização de objetos no livro oficial do Swift da Apple.
Nicolas Miari

Respostas:

121

Vou começar esta resposta na direção oposta: e se você quiser salvar o estado de sua visualização em disco? Isso é conhecido como serialização . O reverso é a desserialização - restaurando o estado do objeto do disco.

O NSCodingprotocolo define dois métodos para serializar e desserializar objetos:

encodeWithCoder(_ aCoder: NSCoder) {
    // Serialize your object here
}

init(coder aDecoder: NSCoder) {
    // Deserialize your object here
}

Então, por que isso é necessário em sua classe personalizada? A resposta é Interface Builder. Quando você arrasta um objeto para um storyboard e o configura, o Interface Builder serializa o estado desse objeto no disco e o desserializa quando o storyboard aparece na tela. Você precisa dizer ao Interface Builder como fazer isso. No mínimo, se você não adicionar nenhuma propriedade nova à sua subclasse, você pode simplesmente pedir à superclasse para fazer o empacotamento e descompactação para você, daí a super.init(coder: aDecoder)chamada. Se sua subclasse for mais complexa, você precisará adicionar seu próprio código de serialização e desserialização para a subclasse.

Isso contrasta com a abordagem do Visual Studio, que é escrever código em um arquivo oculto para criar o objeto em tempo de execução.

Código Diferente
fonte
Por que não colocar tudo dentro de awakeFromNib e esquecer de usar init(coder aCoder : NSCoder)?
Mel
@Honey - em uma palavra, "às vezes você não pode fazer isso". Você normalmente pode, mas nem sempre.
Fattie
@Fattie Os detalhes de não fazer isso são muito complexos ou desnecessários para saber? Se não, você se importa em explicar?
Honey,
9
@Honey se você deseja configurar seu objeto no Interface Builder então awakeFromNibnão funcionará. awakeFromNibé invocado em tempo de execução . Tudo o que você faz no Interface Builder é durante o tempo de design . Para transportar o que você fez em tempo de design para tempo de execução é encodeWithCoder(economia) e init(coder:)(carregamento)
Código diferente
3
@Honey se você não usar o Interface Builder para configurar sua classe personalizada (ou seja, fazê-lo programaticamente com código), você pode fazê-lo em awakeFromNibouinitWIthFrame
Código diferente
28

O requisito para implementar esse inicializador é uma consequência de duas coisas:

  1. O princípio da substituição de Liskov . Se S é uma subclasse de T (por exemplo, MyViewControlleré uma subclasse de ViewController), então os objetos S (instâncias de MyViewController) devem ser substituídos em onde T objetos (instâncias de ViewController) são esperados.

  2. Os inicializadores não são herdados em Swift se algum inicializador for explicitamente definido na subclasse. Se um inicializador for fornecido explicitamente, todos os outros deverão ser fornecidos explicitamente (que podem então apenas chamar super.init(...)). Veja esta questão para justificativa. Está em Java, mas ainda se aplica.

No ponto 1, tudo o que o original ViewControllerpode fazer, a MyViewControllersubclasse deve ser capaz de fazer. Uma dessas coisas é poder ser inicializado a partir de um dado NSCoder. No ponto 2, sua MyViewControllersubclasse não herda automaticamente essa habilidade. Portanto, você deve fornecer manualmente o inicializador que atenda a esse requisito. Nesse caso, você só precisa delegar para a superclasse, para que ela faça o que normalmente faria.

Alexander - Reintegrar Monica
fonte
1
Faz todo o sentido que os construtores não sejam herdados: Se você inicializar uma instância da classe derivada usando o inicializador (herdado) da classe base, as propriedades não herdadas que foram definidas recentemente ("adicionadas") pela classe derivada nunca ser inicializado.
Nicolas Miari
3
Na verdade, os inicializadores são herdados em Swift, visto que você não fornece nenhuma de suas próprias implementações de inicializador em sua subclasse. Se suas propriedades não herdadas recém-definidas tiverem valores padrão, você pode se safar sem escrever nenhum inicializador em sua subclasse e simplesmente herdar todos os inicializadores de sua superclasse. Veja aqui
TheBaj