Como exigir que um protocolo só possa ser adotado por uma classe específica

89

Eu quero este protocolo:

protocol AddsMoreCommands {
     /* ... */
}

apenas para ser adotado por classes que herdam da classe UIViewController. Esta página me diz que posso especificar que ele só é adotado por uma classe (em oposição a uma estrutura) por escrito

protocol AddsMoreCommands: class {
}

mas não consigo ver como exigir que seja adotado apenas por uma classe particular. Essa página fala mais tarde sobre como adicionar wherecláusulas às extensões de protocolo para verificar a conformidade, mas também não consigo ver como adaptar isso.

extension AddsMoreCommands where /* what */ {
}

Existe uma maneira de fazer isso? Obrigado!

emrys57
fonte

Respostas:

114
protocol AddsMoreCommands: class {
    // Code
}

extension AddsMoreCommands where Self: UIViewController {
    // Code
}
Roee84
fonte
4
Eu quase consegui ... Escrevi em selfvez de Self:-( Muito obrigado, funciona bem!
emrys57
Para mim, isso causa alguma estranheza sintática quando uso isso em conjunto com a conversão.
Chris Prince
3
Isso não funcionará se você precisar incluir uma propriedade no protocolo, pois as extensões não podem conter propriedades armazenadas.
shim
1
Pode ter armazenado propriedade você só precisa usar este: objc_getAssociatedObject (self, & KeyName) como? PropertyType
Michał Ziobro
Isso também requer conversão quando uma declaração let / var tem tipo, AddsMoreCommandsmas um método que você passa espera umUIViewController
GoatInTheMachine
77

Isso também pode ser alcançado sem uma extensão:

protocol AddsMoreCommands: class where Self: UIViewController {
   // code
}

EDITADO 04/11/2017 : Como Zig apontou, isso parece gerar um aviso no Xcode 9.1. Atualmente, há um problema relatado no projeto Swift (SR-6265) para remover o aviso. Vou ficar de olho nele e atualizar a resposta de acordo.

EDITADO 29/09/2018 : classé necessário se a variável que irá armazenar a instância precisar ser fraca (como um delegado). Se você não precisa de uma variável fraca, pode omitir o classe apenas escrever o seguinte e não haverá nenhum aviso:

protocol AddsMoreCommands where Self: UIViewController {
   // code
}
rgkobashi
fonte
5
Que coincidência eu clicar em uma pergunta de dois anos e encontrar uma solução perfeita postada uma hora atrás 😲
Oscar Apeland
O Xcode 9.1 agora está me alertando sobre este ditado: Restrição de layout redundante 'Self': 'AnyObject'. Restrição de restrição de layout 'Self': 'AnyObject' implícito aqui. Alterar meu código para o formato da resposta aceita parece ser mais adequado.
Zig
2
A partir do Xcode 9.1, os protocolos somente de classe agora usam em AnyObjectvez de class. protocol AddsMoreCommands: AnyObject where Self: UIViewController { // code }
dodgio
@dodgio ainda recebendo o mesmo aviso usandoAnyObject
rgkobashi
1
@ DávidPásztor você está certo, porém se quiser usá-lo em um padrão estrutural como delegação, para poder tornar a propriedade fraca é necessário adicionar ´classe´ explicitamente :)
rgkobashi
48

Por causa de um problema na resposta anterior, acabei com esta declaração:

protocol AddsMoreCommands where Self : UIViewController { 
    // protocol stuff here  
}

sem avisos no Xcode 9.1

Massmaker
fonte
5
Corrija-me se eu estiver errado, mas o problema com isso em relação à solução acima (que está gerando um aviso no Xcode 9.1 e posteriores), é que você não pode declarar o delegado como fraco?
Kyle Goslan
Além disso, quando eu uso esta solução com o Swift 4.1, preciso lançar as propriedades do AddsMoreCommandspara o UIViewControllerqual eu queria evitar ...
fl034
8
Para evitar o tipo de elenco, você pode fazer o seguinte:typealias AddsMoreCommandsViewController = UIViewController & AddsMoreCommands
plu
33

Agora, no Swift 5, você pode fazer isso:

protocol AddsMoreCommands: UIViewController {
     /* ... */
}

Muito prático.

Wojciech Kulik
fonte