Visão geral:
- Eu tenho um protocolo P1 que fornece uma implementação padrão de uma das funções opcionais do Objective-C.
- Quando eu forneço uma implementação padrão da função opcional, há um aviso
Aviso do compilador:
Non-'@objc' method 'presentationController(_:viewControllerForAdaptivePresentationStyle:)' does not satisfy optional requirement of '@objc' protocol 'UIAdaptivePresentationControllerDelegate'
Versão:
- Swift: 3
- Xcode: 8 (lançamento público)
Tentativas feitas:
- Tentei adicionar,
@objc
mas não ajudou
Questão:
- Como faço para resolver isso?
- Existe uma solução alternativa?
Código:
@objc protocol P1 : UIAdaptivePresentationControllerDelegate {
}
extension P1 where Self : UIViewController {
func presentationController(_ controller: UIPresentationController, viewControllerForAdaptivePresentationStyle style: UIModalPresentationStyle) -> UIViewController? {
return UIViewController()
}
}
class A : UIViewController, P1 {
}
swift
swift3
swift-protocols
user1046037
fonte
fonte
@objc
Respostas:
Embora eu ache que posso responder à sua pergunta, não é uma resposta de que você goste.
TL; DR: as
@objc
funções podem não estar atualmente em extensões de protocolo. Em vez disso, você poderia criar uma classe base, embora essa não seja uma solução ideal.Extensões de protocolo e Objective-C
Em primeiro lugar, esta pergunta / resposta ( Can Swift Method Defined on Extensions on Protocols Accessed in Objective-c ) parece sugerir que, devido à forma como as extensões de protocolo são despachadas sob o capô, os métodos declarados em extensões de protocolo não são visíveis para a
objc_msgSend()
função, e portanto, não são visíveis para o código Objective-C. Uma vez que o método que você está tentando definir em sua extensão precisa ser visível para Objective-C (para queUIKit
possa usá-lo), ele grita com você para não incluir@objc
, mas uma vez que você o inclui, ele grita com você porque@objc
não é permitido em extensões de protocolo. Provavelmente, isso ocorre porque as extensões de protocolo não podem ser visíveis para Objective-C no momento.Também podemos ver que a mensagem de erro uma vez que adicionamos os
@objc
estados "@objc só pode ser usado com membros de classes, protocolos @objc e extensões concretas de classes." Esta não é uma aula; uma extensão para um protocolo @objc não é o mesmo que estar na própria definição de protocolo (ou seja, em requisitos), e a palavra "concreto" sugere que uma extensão de protocolo não conta como uma extensão de classe concreta.Gambiarra
Infelizmente, isso impede completamente que você use extensões de protocolo quando as implementações padrão devem ser visíveis para estruturas Objective-C. No início, pensei que talvez
@objc
não fosse permitido em sua extensão de protocolo porque o Compilador Swift não poderia garantir que os tipos em conformidade seriam classes (mesmo que você tenha especificado especificamenteUIViewController
). Então eu coloquei umclass
requisitoP1
. Isso não funcionou.Talvez a única solução alternativa seja simplesmente usar uma classe base em vez de um protocolo aqui, mas isso obviamente não é completamente ideal porque uma classe pode ter apenas uma única classe base, mas estar em conformidade com vários protocolos.
Se você escolher seguir esse caminho, leve esta questão ( Método de protocolo opcional Swift 3 ObjC não chamado na subclasse ) em consideração. Parece que outro problema atual no Swift 3 é que as subclasses não herdam automaticamente as implementações de requisitos de protocolo opcionais de sua superclasse. A resposta a essas perguntas usa uma adaptação especial de
@objc
para contorná-la.Relatando o problema
Acho que isso já está sendo discutido entre aqueles que trabalham nos projetos de código aberto do Swift, mas você pode ter certeza que eles estão cientes usando o Bug Reporter da Apple , que provavelmente acabaria chegando à equipe do Swift Core, ou o relator de bug do Swift . No entanto, qualquer um deles pode achar seu bug muito amplo ou já conhecido. A equipe do Swift também pode considerar o que você está procurando como um novo recurso de idioma; nesse caso, você deve primeiro verificar as listas de discussão .
Atualizar
Em dezembro de 2016, esse problema foi relatado à comunidade Swift. O problema ainda está marcado como aberto com prioridade média, mas o seguinte comentário foi adicionado:
Como seu protocolo está no mesmo módulo que sua extensão, no entanto, você poderá fazer isso em uma versão futura do Swift.
Atualização 2
Em fevereiro de 2017, esse problema foi oficialmente encerrado como "Não vou fazer" por um dos membros da equipe central da Swift com a seguinte mensagem:
Estender
NSObject
ou mesmoUIViewController
não vai realizar exatamente o que você deseja, mas infelizmente não parece que será possível.Em um futuro de (muito) longo prazo, podemos ser capazes de eliminar
@objc
totalmente a dependência de métodos, mas esse momento provavelmente não chegará tão cedo, uma vez que as estruturas do Cocoa não são atualmente escritas em Swift (e não podem ser até que tenha uma ABI estável) .Atualização 3
A partir do outono de 2019, isso está se tornando um problema menor porque mais e mais frameworks da Apple estão sendo escritos em Swift. Por exemplo, se você usar em
SwiftUI
vez deUIKit
, contorna o problema totalmente porque@objc
nunca seria necessário ao se referir a umSwiftUI
método.As estruturas da Apple escritas em Swift incluem:
Seria de se esperar que esse padrão continuasse ao longo do tempo, agora que o Swift é oficialmente ABI e módulo estável a partir do Swift 5.0 e 5.1, respectivamente.
fonte
Swift 4
não haver outra alternativa no momento.Acabei de encontrar isso depois de habilitar 'estabilidade de módulo' (ativando 'Construir bibliotecas para distribuição') em uma estrutura rápida que uso.
O que eu tinha era algo assim:
A função na extensão apresentou estes erros:
O método de instância '@objc' na extensão da subclasse de 'LessAwesomeClass' requer iOS 13.0.0
Não - método '@ objc' 'niceDelegateFunc' não satisfaz o requisito do protocolo '@objc' 'GreatDelegate'
Mover as funções para a classe em vez de para uma extensão resolveu o problema.
fonte
Aqui está outra solução alternativa. Eu também encontrei esse problema e não consigo mudar do UIKit para o SwiftUI ainda. Mover as implementações padrão para uma classe base comum também não era uma opção para mim. Minhas implementações padrão eram bastante extensas, então eu realmente não queria ter todo aquele código duplicado. A solução alternativa que acabei usando foi usar funções de wrapper no protocolo e, em seguida, simplesmente chamar essas funções de cada classe. Não é bonito, mas pode ser melhor que a alternativa, dependendo da situação. Seu código ficaria assim:
fonte