Em Swift, posso definir explicitamente o tipo de uma variável, declarando-a da seguinte maneira:
var object: TYPE_NAME
Se quisermos dar um passo adiante e declarar uma variável em conformidade com vários protocolos, podemos usar o protocol
declarativo:
var object: protocol<ProtocolOne,ProtocolTwo>//etc
E se eu quiser declarar um objeto que está em conformidade com um ou mais protocolos e também é de um tipo de classe base específico? O equivalente em Objective-C seria assim:
NSSomething<ABCProtocolOne,ABCProtocolTwo> * object = ...;
Em Swift, eu esperaria que fosse assim:
var object: TYPE_NAME,ProtocolOne//etc
Isso nos dá a flexibilidade de sermos capazes de lidar com a implementação do tipo de base, bem como a interface adicionada definida no protocolo.
Existe outra maneira mais óbvia que eu possa estar perdendo?
Exemplo
Por exemplo, digamos que eu tenha uma UITableViewCell
fábrica responsável por devolver células em conformidade com um protocolo. Podemos facilmente configurar uma função genérica que retorna células em conformidade com um protocolo:
class CellFactory {
class func createCellForItem<T: UITableViewCell where T:MyProtocol >(item: SpecialItem,tableView: UITableView) -> T {
//etc
}
}
mais tarde, quero retirar da fila essas células, aproveitando o tipo e o protocolo
var cell: MyProtocol = CellFactory.createCellForItem(somethingAtIndexPath) as UITableViewCell
Isso retorna um erro porque uma célula de exibição de tabela não está em conformidade com o protocolo ...
Gostaria de poder especificar que a célula é um UITableViewCell
e está em conformidade com o MyProtocol
na declaração da variável?
Justificação
Se você estiver familiarizado com o Factory Pattern, isso faria sentido no contexto de ser capaz de retornar objetos de uma classe específica que implementa uma determinada interface.
Assim como no meu exemplo, às vezes gostamos de definir interfaces que fazem sentido quando aplicadas a um objeto específico. Meu exemplo de célula de exibição de tabela é uma dessas justificativas.
Embora o tipo fornecido não esteja exatamente em conformidade com a interface mencionada, o objeto que a fábrica retorna está e, por isso, gostaria de ter flexibilidade para interagir com o tipo de classe base e a interface de protocolo declarada
fonte
NSSomething<ABCProtocolOne,ABCProtocolTwo> * object = ...;
. Este objeto parece totalmente inútil, poisNSSomething
já sabe a que se conforma. Se ele não estiver em conformidade com um dos protocolos em,<>
você teráunrecognised selector ...
travamentos. Isso não fornece nenhuma segurança de tipo.Respostas:
No Swift 4 agora é possível declarar uma variável que é uma subclasse de um tipo e implementa um ou mais protocolos ao mesmo tempo.
Para fazer uma variável opcional:
ou como parâmetro de um método:
A Apple anunciou isso na WWDC 2017 na Sessão 402: O que há de novo no Swift
fonte
Você não pode declarar variável como
nem declarar tipo de retorno de função como
Você pode declarar como um parâmetro de função como este, mas é basicamente um up-casting.
A partir de agora, tudo que você pode fazer é:
Com isso,
cell
é tecnicamente idêntico aasProtocol
.Mas, quanto ao compilador,
cell
possui interface deUITableViewCell
apenas, enquantoasProtocol
possui apenas interface de protocolos. Então, quando você quiser chamarUITableViewCell
os métodos de, você deve usar acell
variável. Quando você quiser chamar o método de protocolos, use aasProtocol
variável.Se você tiver certeza de que a célula está em conformidade com os protocolos que você não precisa usar
if let ... as? ... {}
. gostar:fonte
-> UITableViewCell<MyProtocol>
, isso é inválido, porqueUITableViewCell
não é um tipo genérico. Acho que nem mesmo compila.cell
tem apenas métodos de protocolos (para compilador).Infelizmente, o Swift não oferece suporte à conformidade de protocolo em nível de objeto. No entanto, há uma solução um tanto estranha que pode servir aos seus propósitos.
Então, em qualquer lugar que você precise fazer qualquer coisa que UIViewController tenha, você acessará o aspecto .viewController da estrutura e qualquer coisa que você precisar do aspecto de protocolo, você fará referência ao .protocol.
Por exemplo:
Agora, sempre que você precisar de mySpecialViewController para fazer qualquer coisa relacionada ao UIViewController, basta fazer referência a mySpecialViewController.viewController e sempre que precisar fazer alguma função de protocolo, consulte mySpecialViewController.protocol.
Esperançosamente, o Swift 4 nos permitirá declarar um objeto com protocolos anexados a ele no futuro. Mas, por enquanto, isso funciona.
Espero que isto ajude!
fonte
Talvez eu esteja enganado, mas você não está falando sobre adicionar conformidade de protocolo à
UITableCellView
classe? O protocolo é, nesse caso, estendido à classe base, e não ao objeto. Consulte a documentação da Apple sobre como declarar a adoção de protocolo com uma extensão que, no seu caso, seria algo como:Além da documentação do Swift já referenciada, consulte também o artigo de Nate Cooks Funções genéricas para tipos incompatíveis com outros exemplos.
A adoção de protocolo fará exatamente isso, fará um objeto aderir ao protocolo fornecido. No entanto, esteja ciente do lado adverso, que uma variável de um determinado tipo de protocolo não conhece nada fora do protocolo. Mas isso pode ser contornado definindo um protocolo que tem todos os métodos / variáveis necessários / ...
Se você quiser que um método genérico, variável esteja em conformidade com um protocolo e tipos de classe base, você pode estar sem sorte. Mas parece que você precisa definir o protocolo amplo o suficiente para ter os métodos de conformidade necessários e, ao mesmo tempo, estreito o suficiente para ter a opção de adotá-lo para classes básicas sem muito trabalho (ou seja, apenas declarar que uma classe está em conformidade com o protocolo).
fonte
Certa vez, tive uma situação semelhante ao tentar vincular minhas conexões de interator genérico em Storyboards (o IB não permite que você conecte saídas a protocolos, apenas instâncias de objeto), que contornei simplesmente mascarando a classe base pública ivar com um privado computado propriedade. Embora isso não impeça alguém de fazer atribuições ilegais por si só, ele fornece uma maneira conveniente de evitar com segurança qualquer interação indesejada com uma instância não conforme em tempo de execução. (ou seja, evite chamar métodos delegados para objetos que não estejam em conformidade com o protocolo.)
Exemplo:
O "outputReceiver" é declarado opcional, assim como o "protocolOutputReceiver" privado. Sempre acessando o outputReceiver (também conhecido como delegado) por meio do último (a propriedade computada), eu efetivamente filtro quaisquer objetos que não estejam em conformidade com o protocolo. Agora posso simplesmente usar o encadeamento opcional para chamar com segurança o objeto delegado, quer ele implemente o protocolo ou exista ou não.
Para aplicar isso à sua situação, você pode fazer com que o ivar público seja do tipo "YourBaseClass?" (ao contrário de AnyObject) e use a propriedade computada privada para impor a conformidade do protocolo. FWIW.
fonte