É possível em Swift? Caso contrário, existe uma solução alternativa para fazer isso?
É possível em Swift? Caso contrário, existe uma solução alternativa para fazer isso?
Respostas:
protocol MyProtocol {
func doSomething()
}
extension MyProtocol {
func doSomething() {
/* return a default value or just leave empty */
}
}
struct MyStruct: MyProtocol {
/* no compile error */
}
Vantagens
Nenhum tempo de execução do Objective-C está envolvido (bem, pelo menos não explicitamente). Isso significa que você pode conformar estruturas, enumerações e não NSObject
classes a ele. Além disso, isso significa que você pode tirar proveito do poderoso sistema de genéricos.
Você sempre pode ter certeza de que todos os requisitos sejam atendidos ao encontrar tipos que estejam em conformidade com esse protocolo. É sempre uma implementação concreta ou padrão. É assim que "interfaces" ou "contratos" se comportam em outros idiomas.
Desvantagens
Para não- Void
requisitos, você precisa ter um valor padrão razoável , o que nem sempre é possível. No entanto, quando você encontra esse problema, significa que esse requisito realmente não deve ter uma implementação padrão ou que você cometeu um erro durante o design da API.
Você não pode distinguir entre uma implementação padrão e nenhuma implementação , pelo menos sem resolver esse problema com valores de retorno especiais. Considere o seguinte exemplo:
protocol SomeParserDelegate {
func validate(value: Any) -> Bool
}
Se você fornecer uma implementação padrão que apenas retorna true
- tudo bem à primeira vista. Agora, considere o seguinte pseudo-código:
final class SomeParser {
func parse(data: Data) -> [Any] {
if /* delegate.validate(value:) is not implemented */ {
/* parse very fast without validating */
} else {
/* parse and validate every value */
}
}
}
Não há como implementar essa otimização - você não pode saber se seu delegado implementa um método ou não.
Embora haja várias maneiras diferentes de superar esse problema (usando fechamentos opcionais, objetos delegados diferentes para operações diferentes, entre outros), esse exemplo apresenta o problema claramente.
@objc optional
.@objc protocol MyProtocol {
@objc optional func doSomething()
}
class MyClass: NSObject, MyProtocol {
/* no compile error */
}
Vantagens
Desvantagens
Isso limita severamente os recursos do seu protocolo, exigindo que todos os tipos em conformidade sejam compatíveis com Objective-C. Isso significa que apenas as classes herdadas NSObject
podem estar em conformidade com esse protocolo. Sem estruturas, sem enumerações, sem tipos associados.
Você sempre deve verificar se um método opcional é implementado , opcionalmente, chamando ou verificando se o tipo conforme o implementa. Isso pode introduzir muitos clichês se você estiver chamando métodos opcionais com frequência.
respondsToSelector
?
optional func doSomething(param: Int?)
No Swift 2 e em diante, é possível adicionar implementações padrão de um protocolo. Isso cria uma nova maneira de métodos opcionais em protocolos.
protocol MyProtocol {
func doSomethingNonOptionalMethod()
func doSomethingOptionalMethod()
}
extension MyProtocol {
func doSomethingOptionalMethod(){
// leaving this empty
}
}
Não é uma maneira muito boa de criar métodos opcionais de protocolo, mas oferece a possibilidade de usar estruturas em retornos de chamada de protocolo.
Eu escrevi um pequeno resumo aqui: https://www.avanderlee.com/swift-2-0/optional-protocol-methods/
Como existem algumas respostas sobre como usar o modificador opcional e o atributo @objc para definir o protocolo de requisitos opcional, darei uma amostra sobre como usar extensões de protocolo para definir o protocolo opcional.
O código abaixo é o Swift 3. *.
/// Protocol has empty default implementation of the following methods making them optional to implement:
/// `cancel()`
protocol Cancelable {
/// default implementation is empty.
func cancel()
}
extension Cancelable {
func cancel() {}
}
class Plane: Cancelable {
//Since cancel() have default implementation, that is optional to class Plane
}
let plane = Plane()
plane.cancel()
// Print out *United Airlines can't cancelable*
Observe que os métodos de extensão de protocolo não podem ser invocados pelo código Objective-C e, pior ainda, a equipe Swift não o corrigirá. https://bugs.swift.org/browse/SR-492
As outras respostas aqui envolvendo a marcação do protocolo como "@objc" não funcionam ao usar tipos específicos rápidos.
struct Info {
var height: Int
var weight: Int
}
@objc protocol Health {
func isInfoHealthy(info: Info) -> Bool
}
//Error "Method cannot be marked @objc because the type of the parameter cannot be represented in Objective-C"
Para declarar protocolos opcionais que funcionam bem com o swift, declare as funções como variáveis em vez de func.
protocol Health {
var isInfoHealthy: (Info) -> (Bool)? { get set }
}
E, em seguida, implemente o protocolo da seguinte maneira
class Human: Health {
var isInfoHealthy: (Info) -> (Bool)? = { info in
if info.weight < 200 && info.height > 72 {
return true
}
return false
}
//Or leave out the implementation and declare it as:
//var isInfoHealthy: (Info) -> (Bool)?
}
Você pode usar "?" para verificar se a função foi ou não implementada
func returnEntity() -> Health {
return Human()
}
var anEntity: Health = returnEntity()
var isHealthy = anEntity.isInfoHealthy(Info(height: 75, weight: 150))?
//"isHealthy" is true
Aqui está um exemplo concreto com o padrão de delegação.
Configure seu protocolo:
@objc protocol MyProtocol:class
{
func requiredMethod()
optional func optionalMethod()
}
class MyClass: NSObject
{
weak var delegate:MyProtocol?
func callDelegate()
{
delegate?.requiredMethod()
delegate?.optionalMethod?()
}
}
Defina o delegado para uma classe e implemente o protocolo. Veja que o método opcional não precisa ser implementado.
class AnotherClass: NSObject, MyProtocol
{
init()
{
super.init()
let myInstance = MyClass()
myInstance.delegate = self
}
func requiredMethod()
{
}
}
Uma coisa importante é que o método opcional é opcional e precisa de um "?" ao ligar. Mencione o segundo ponto de interrogação.
delegate?.optionalMethod?()
No Swift 3.0
@objc protocol CounterDataSource {
@objc optional func increment(forCount count: Int) -> Int
@objc optional var fixedIncrement: Int { get }
}
Isso economizará seu tempo.
@objc
é necessária em todos os membros agora?
required
flag, mas com erros: required
só pode ser usado em declarações 'init'.
optional
palavra-chave antes de cada método.@objc
, não apenas o protocolo.
Para ilustrar a mecânica da resposta de Antoine:
protocol SomeProtocol {
func aMethod()
}
extension SomeProtocol {
func aMethod() {
print("extensionImplementation")
}
}
class protocolImplementingObject: SomeProtocol {
}
class protocolImplementingMethodOverridingObject: SomeProtocol {
func aMethod() {
print("classImplementation")
}
}
let noOverride = protocolImplementingObject()
let override = protocolImplementingMethodOverridingObject()
noOverride.aMethod() //prints "extensionImplementation"
override.aMethod() //prints "classImplementation"
Penso que antes de perguntar como você pode implementar um método de protocolo opcional, você deve estar se perguntando por que deveria implementar um.
Se pensarmos em protocolos rápidos como uma interface na programação clássica orientada a objetos, métodos opcionais não fazem muito sentido, e talvez uma solução melhor seria criar implementação padrão ou separar o protocolo em um conjunto de protocolos (talvez com algumas relações de herança entre eles) para representar a possível combinação de métodos no protocolo.
Para uma leitura mais detalhada, consulte https://useyourloaf.com/blog/swift-optional-protocol-methods/ , que fornece uma excelente visão geral sobre esse assunto.
Um pouco fora do tópico da pergunta original, mas parte da ideia de Antoine e achei que poderia ajudar alguém.
Você também pode tornar as propriedades computadas opcionais para estruturas com extensões de protocolo.
Você pode tornar uma propriedade no protocolo opcional
protocol SomeProtocol {
var required: String { get }
var optional: String? { get }
}
Implemente a propriedade computada fictícia na extensão de protocolo
extension SomeProtocol {
var optional: String? { return nil }
}
E agora você pode usar estruturas que possuem ou não a propriedade opcional implementada
struct ConformsWithoutOptional {
let required: String
}
struct ConformsWithOptional {
let required: String
let optional: String?
}
Também escrevi como fazer propriedades opcionais nos protocolos Swift no meu blog , que vou manter atualizado caso as coisas mudem com os lançamentos do Swift 2.
Como criar métodos delegados opcionais e necessários.
@objc protocol InterViewDelegate:class {
@objc optional func optfunc() // This is optional
func requiredfunc()// This is required
}
Aqui está um exemplo muito simples para Classes rápidas, e não para estruturas ou enumerações. Observe que o método do protocolo, sendo opcional, possui dois níveis de encadeamento opcional em ação. Além disso, a classe que adota o protocolo precisa do atributo @objc em sua declaração.
@objc protocol CollectionOfDataDelegate{
optional func indexDidChange(index: Int)
}
@objc class RootView: CollectionOfDataDelegate{
var data = CollectionOfData()
init(){
data.delegate = self
data.indexIsNow()
}
func indexDidChange(index: Int) {
println("The index is currently: \(index)")
}
}
class CollectionOfData{
var index : Int?
weak var delegate : CollectionOfDataDelegate?
func indexIsNow(){
index = 23
delegate?.indexDidChange?(index!)
}
}
delegate?.indexDidChange?(index!)
:?
protocol CollectionOfDataDelegate{ func indexDidChange(index: Int) }
então você o chamaria sem o ponto de interrogação: delegate?.indexDidChange(index!)
Quando você define um requisito opcional para um método em um protocolo, o Tipo que se conforma a ele NÃO pode implementar esse método , então ?
é usado para verificar a implementação e, se não houver, o programa não falhará. @Unheilig
weak var delegate : CollectionOfDataDelegate?
(assegurar referência fraca?)
delegate?
uso à sua resposta? Essa informação deve realmente pertencer a outras pessoas no futuro. Eu quero aprovar isso, mas essa informação deve realmente estar na resposta.
se você quiser fazê-lo em puro swift, a melhor maneira é fornecer uma partição padrão de implementação se você retornar um tipo Swift, como por exemplo struct com tipos Swift
exemplo:
struct magicDatas {
var damagePoints : Int?
var manaPoints : Int?
}
protocol magicCastDelegate {
func castFire() -> magicDatas
func castIce() -> magicDatas
}
extension magicCastDelegate {
func castFire() -> magicDatas {
return magicDatas()
}
func castIce() -> magicDatas {
return magicDatas()
}
}
então você pode implementar o protocolo sem definir todas as funções
Existem duas maneiras de criar um método opcional no protocolo rápido.
1 - A primeira opção é marcar o seu protocolo usando o atributo @objc. Enquanto isso significa que ele pode ser adotado apenas por classes, significa que você marca métodos individuais como opcionais como este:
@objc protocol MyProtocol {
@objc optional func optionalMethod()
}
2 - Uma maneira mais rápida: Esta opção é melhor. Escreva implementações padrão dos métodos opcionais que não fazem nada, como este.
protocol MyProtocol {
func optionalMethod()
func notOptionalMethod()
}
extension MyProtocol {
func optionalMethod() {
//this is a empty implementation to allow this method to be optional
}
}
O Swift possui um recurso chamado extension que nos permite fornecer uma implementação padrão para os métodos que queremos que sejam opcionais.
Uma opção é armazená-los como variáveis de função opcionais:
struct MyAwesomeStruct {
var myWonderfulFunction : Optional<(Int) -> Int> = nil
}
let squareCalculator =
MyAwesomeStruct(myWonderfulFunction: { input in return input * input })
let thisShouldBeFour = squareCalculator.myWonderfulFunction!(2)
Defina a função no protocolo e crie extensão para esse protocolo e crie uma implementação vazia para a função que você deseja usar como opcional.
Para definir Optional
Protocol
rapidamente, você deve usar a @objc
palavra-chave antes da Protocol
declaração e attribute
/ method
declaração dentro desse protocolo. Abaixo está uma amostra da propriedade opcional de um protocolo.
@objc protocol Protocol {
@objc optional var name:String?
}
class MyClass: Protocol {
// No error
}
Coloque @optional
na frente de métodos ou propriedades.
@optional
nem sequer é a palavra-chave correta. É optional
, e você deve declarar a classe e o protocolo com o @objc
atributo