Eu costumo colocar apenas as necessidades (propriedades armazenadas, inicializadores) nas definições de minha classe e mover todo o resto para elas próprias extension
, como um extension
bloco lógico que eu agruparia // MARK:
também.
Para uma subclasse UIView, por exemplo, eu terminaria com uma extensão para coisas relacionadas ao layout, uma para assinar e manipular eventos e assim por diante. Nessas extensões, inevitavelmente tenho que substituir alguns métodos do UIKit, por exemplo layoutSubviews
. Eu nunca notei nenhum problema com essa abordagem - até hoje.
Veja esta hierarquia de classes, por exemplo:
public class C: NSObject {
public func method() { print("C") }
}
public class B: C {
}
extension B {
override public func method() { print("B") }
}
public class A: B {
}
extension A {
override public func method() { print("A") }
}
(A() as A).method()
(A() as B).method()
(A() as C).method()
A saída é A B C
. Isso faz pouco sentido para mim. Eu li sobre as Extensões de Protocolo sendo enviadas estaticamente, mas isso não é um protocolo. Esta é uma classe regular e espero que as chamadas de método sejam dinamicamente despachadas em tempo de execução. Claramente, a chamada C
deve ser pelo menos dinamicamente despachada e produzida C
?
Se eu remover a herança NSObject
e criar C
uma classe raiz, o compilador reclama dizendo declarations in extensions cannot override yet
, sobre o qual eu já li. Mas como ter NSObject
como classe raiz muda as coisas?
Mover as duas substituições para a declaração de classe produz A A A
como esperado, mover apenas B
produz A B B
, mover somente A
produz C B C
, o último dos quais não faz absolutamente nenhum sentido para mim: nem mesmo aquele digitado estaticamente para A
produzir mais a A
saída!
Adicionar a dynamic
palavra-chave à definição ou a uma substituição parece me fornecer o comportamento desejado 'a partir desse ponto na hierarquia de classes para baixo' ...
Vamos mudar nosso exemplo para algo um pouco menos construído, o que realmente me fez postar esta pergunta:
public class B: UIView {
}
extension B {
override public func layoutSubviews() { print("B") }
}
public class A: B {
}
extension A {
override public func layoutSubviews() { print("A") }
}
(A() as A).layoutSubviews()
(A() as B).layoutSubviews()
(A() as UIView).layoutSubviews()
Agora chegamos A B A
. Aqui não posso dinamizar o layoutSubviews do UIView por qualquer meio.
Mover as duas substituições para a declaração de classe nos leva A A A
novamente, apenas A ou B ainda nos recebem A B A
. dynamic
novamente resolve meus problemas.
Em teoria, eu poderia acrescentar dynamic
tudo override
o que faço, mas sinto que estou fazendo outra coisa errada aqui.
É realmente errado usar extension
s para agrupar código como eu?
fonte
Respostas:
As extensões não podem / não devem substituir.
Não é possível substituir a funcionalidade (como propriedades ou métodos) em extensões, conforme documentado no Swift Guide da Apple.
Guia do desenvolvedor do Swift
O compilador está permitindo substituir a extensão para compatibilidade com o Objective-C. Mas na verdade está violando a diretiva de linguagem.
Isso me lembrou as " Três Leis da Robótica " de Isaac Asimov.
Extensões ( açúcar sintático ) definem métodos independentes que recebem seus próprios argumentos. A função solicitada, ou seja,
layoutSubviews
depende do contexto que o compilador conhece quando o código é compilado. O UIView herda de UIResponder, que herda de NSObject, portanto a substituição na extensão é permitida, mas não deve ser .Portanto, não há nada errado com o agrupamento, mas você deve substituir a classe e não a extensão.
Notas da diretiva
Você pode apenas
override
um método de superclasse, ou seja,load()
initialize()
em uma extensão de uma subclasse se o método for compatível com Objective-C.Portanto, podemos dar uma olhada no porquê de permitir que você compile usando
layoutSubviews
.Todos os aplicativos Swift são executados no tempo de execução do Objective-C, exceto quando se utiliza estruturas puras somente do Swift, que permitem um tempo de execução apenas do Swift.
Como descobrimos, o tempo de execução do Objective-C geralmente chama dois métodos principais de classe
load()
einitialize()
automaticamente ao inicializar as classes nos processos do seu aplicativo.Em relação ao
dynamic
modificadorNa Biblioteca do desenvolvedor da Apple (archive.org)
Você pode usar o
dynamic
modificador para exigir que o acesso aos membros seja enviado dinamicamente por meio do tempo de execução do Objective-C.Quando as APIs Swift são importadas pelo tempo de execução do Objective-C, não há garantias de despacho dinâmico para propriedades, métodos, subscritos ou inicializadores. O compilador Swift ainda pode desvirtualizar ou incorporar o acesso de membros para otimizar o desempenho do seu código, ignorando o tempo de execução do Objective-C. 😳
Portanto,
dynamic
pode ser aplicado ao seulayoutSubviews
->,UIView Class
pois é representado pelo Objective-C e o acesso a esse membro é sempre usado usando o tempo de execução do Objective-C.É por isso que o compilador permite que você use
override
edynamic
.fonte
Um dos objetivos do Swift é o envio estático, ou melhor, a redução do envio dinâmico. Obj-C, no entanto, é uma linguagem muito dinâmica. A situação que você está vendo decorre do vínculo entre os dois idiomas e a maneira como eles trabalham juntos. Realmente não deveria compilar.
Um dos pontos principais sobre as extensões é que elas são para extensão, não para substituição / substituição. Está claro, tanto pelo nome quanto pela documentação, que essa é a intenção. De fato, se você remover o link para Obj-C do seu código (remover
NSObject
como superclasse), ele não será compilado.Portanto, o compilador está tentando decidir o que ele pode despachar estaticamente e o que ele deve despachar dinamicamente, e está caindo em uma lacuna devido ao link Obj-C no seu código. O motivo
dynamic
'funciona' é porque está forçando a vinculação de Obj-C em tudo, para que tudo seja sempre dinâmico.Portanto, não é errado usar extensões para agrupar, isso é ótimo, mas é errado substituir as extensões. Quaisquer substituições devem estar na própria classe principal e chamar pontos de extensão.
fonte
supportedInterfaceOrientations
noUINavigationController
(para fins de mostrar diferentes pontos de vista em diferentes orientações), você deve usar uma classe personalizada não uma extensão? Muitas respostas sugerem o uso de uma extensão para substituir,supportedInterfaceOrientations
mas gostariam de esclarecimentos. Obrigado!Existe uma maneira de conseguir uma separação limpa da assinatura e implementação da classe (em extensões), mantendo a capacidade de ter substituições nas subclasses. O truque é usar variáveis no lugar das funções
Se você definir cada subclasse em um arquivo de origem rápido separado, poderá usar variáveis computadas para as substituições, mantendo a implementação correspondente organizada de maneira limpa em extensões. Isso contornará as "regras" de Swift e tornará a API / assinatura da sua classe organizada em um só lugar:
...
...
A extensão de cada classe pode usar os mesmos nomes de método para a implementação porque são particulares e não são visíveis uma à outra (desde que estejam em arquivos separados).
Como você pode ver, a herança (usando o nome da variável) funciona corretamente usando super.variablename
fonte
Essa resposta não foi direcionada ao OP, exceto pelo fato de eu me sentir inspirado a responder por sua afirmação: "Costumo apenas colocar as necessidades (propriedades armazenadas, inicializadores) nas definições de minha classe e mover todo o resto para sua própria extensão. .. " Sou principalmente um programador de C # e, em C #, é possível usar classes parciais para esse fim. Por exemplo, o Visual Studio coloca o material relacionado à interface do usuário em um arquivo de origem separado usando uma classe parcial e deixa seu arquivo de origem principal organizado, para que você não tenha essa distração.
Se você procurar por "classe parcial rápida", encontrará vários links nos quais os adeptos do Swift dizem que o Swift não precisa de classes parciais porque você pode usar extensões. Curiosamente, se você digitar "extensão rápida" no campo de pesquisa do Google, sua primeira sugestão de pesquisa será "substituição rápida da extensão" e, no momento, essa pergunta de estouro de pilha é o primeiro hit. Entendo que isso significa que problemas com (falta de) recursos de substituição são o tópico mais procurado relacionado às extensões Swift e destaca o fato de que as extensões Swift não podem substituir classes parciais, pelo menos se você usar classes derivadas em seu programação.
De qualquer forma, para encurtar uma introdução demorada, encontrei esse problema em uma situação em que queria mover alguns métodos padrão / bagagem dos principais arquivos de origem das classes Swift que meu programa C # para Swift estava gerando. Depois de me deparar com o problema de nenhuma substituição permitida para esses métodos, depois de movê-los para extensões, acabei implementando a seguinte solução simples. Os principais arquivos de origem Swift ainda contêm alguns métodos stub minúsculos que chamam os métodos reais nos arquivos de extensão, e esses métodos de extensão recebem nomes exclusivos para evitar o problema de substituição.
.
.
.
.
Como eu disse na minha introdução, isso realmente não responde à pergunta do OP, mas espero que essa solução simplória possa ser útil para outras pessoas que desejam mover métodos dos principais arquivos de origem para arquivos de extensão e executar o não problema de substituição.
fonte
Use POP (Programação Orientada a Protocolo) para substituir funções em extensões.
fonte