web-dev-qa-db-fra.com

Comment passer un protocole en paramètre dans Swift

En Objective-C, je sais comment passer une protocol en paramètre:

- (void)MyMethod:(Protocol *)myparameter

Mais dans Swift, il n'y a plus de type Protocol.

Comment puis-je passer un protocole en tant que paramètre sans savoir lequel?

17
Jean Lebrument

Dans l'un de vos commentaires, vous dites:

"Je souhaite créer une méthode qui retourne un tableau de type de classe qui implémente le protocole souhaité."

Avez-vous essayé quelque chose comme ce qui suit:

//notice the use of @objc here
@objc protocol AlertProtocol
{
    func getMyName()->String
}

class Class1 : AlertProtocol
{
     let name = "Object 1"
     func getMyName() -> String
    {
        return name
    }
}

class Class2 : AlertProtocol
{
    let name = "Object 2"
    func getMyName() -> String
    {
        return name
    }
}

//borrowing from and refactoring siLo's answer
func classesConformingToProtocol(proto:Protocol) -> [AnyClass]
{
    let availableClasses : [AnyClass] = [ Class1.self, Class2.self ]

    var conformingClasses = Array<AnyClass>()

    for myClass : AnyClass in availableClasses
    {
        if myClass.conforms(to: proto)
        {
            conformingClasses.append(myClass)
        }
    }

    return conformingClasses
}

Ensuite, utilisez la structure ci-dessus comme ceci:

let classes = classesConformingToProtocol(AlertProtocol.self)

La partie délicate qui fait le travail est le "@objc" qui expose le protocole au runtime Objective C et nous permet de passer tout "Type de protocole" en tant que paramètre.

Probablement qu'à l'avenir, nous pourrons le faire d'une manière "pure" et rapide.

11
joakim

Voici ce que j'ai essayé:

@objc protocol Walker
{
    func walk()
}

@objc protocol Runner
{
    func run()
}

@objc class Zombie : Walker
{
    func walk () { println("Brains...") }
}

@objc class Survivor : Runner
{
    func run() { println("Aaaah, zombies!") }
}

func classesConformingToProtocol(proto:Protocol) -> AnyClass[]
{
    let availableClasses : AnyClass[] = [ Zombie.self, Survivor.self ]

    var conformingClasses = Array<AnyClass>()

    for myClass : AnyClass in availableClasses
    {
        if myClass.conformsToProtocol(proto)
        {
            conformingClasses.append(myClass)
        }
    }

    return conformingClasses
}

// This does not work
let walkers = classesConformingToProtocol(Walker.self)
let runners = classesConformingToProtocol(Runner.self)

Je n'ai pas pu convertir les informations Metatype de Swift en un objet Protocol.

4
Erik

Dans Swift 2.0, je l'utilise comme ça avant:

classA.conformsToProtocol(XXXProtocol.self as! Protocol)

Ça ne marche pas bien ...

Regardez la définition de Protocol:

// All methods of class Protocol are unavailable. 
// Use the functions in objc/runtime.h instead.

@available(iOS 2.0, *)
public class Protocol {
}

Tous ne sont pas disponibles ... et je ne sais pas lequel utiliser à la place dans objc/runtime.h

Je dois donc utiliser cette méthode:

if ClassA is protocol<XXXProtocol> {
    // do something
}

Actuellement, ça marche ...

1
Klein Mioke

Si vous n'autorisez pas l'utilisation de @objc (parce que vos protocoles ont une propriété, par exemple), la seule solution que j'ai trouvée concerne la fermeture. Ensuite, vous devez utiliser une fermeture pour utiliser un protocole et renvoyer une valeur.

protocol Proto { }
protocol Proto2 { }
class Foo: Proto { }
class Bar: Proto, Proto2 { }
class Baz: Proto2 { }
class Qux { }

func printConforms(classList: [AnyClass], protoCond: (AnyClass) -> Any?) {
    for i in classList {
        print(i, terminator: " -> ")
        if protoCond(i) != nil {
            print("is subscriber")
        } else {
            print("NOT IS subscriber")
        }
    }
}

let myClasses: [AnyClass] = [Foo.self, Bar.self, Baz.self, Qux.self]
printConforms(classList: myClasses, protoCond: { $0 as? Proto.Type })

Exemple plus complet: https://Gist.github.com/brunomacabeusbr/eea343bb9119b96eed3393e41dcda0c9

Modifier

Une autre meilleure solution consiste à utiliser des génériques, par exemple:

protocol Proto { }
class Foo: Proto { }
class Bar: Proto { }
class Baz { }

func filter<T>(classes: [AnyClass], byConformanceTo: T.Type) -> [AnyClass] {
    return classes.filter { $0 is T }
}

filter(classes: [Foo.self, Bar.self, Baz.self], byConformanceTo: Proto.Type.self)
// return [Foo.self, Bar.self]
0
Macabeus