web-dev-qa-db-fra.com

Comment appeler une fonction statique sur un protocole de manière générique?

Est-il utile de déclarer une fonction statique sur un protocole? Le client utilisant le protocole doit appeler la fonction sur un type conforme au protocole, n'est-ce pas? Cela rompt l’idée de ne pas avoir à connaître le type conforme au protocole OMI. Existe-t-il un moyen d'appeler la fonction statique sur le protocole de manière à ne pas avoir à connaître le type réel conforme à mon protocole?

19
SirRupertIII

Bonne question. Voici mon humble point de vue:

Est-il utile de déclarer une fonction statique sur un protocole?

Quasiment pareil à avoir des méthodes d'instance déclarées dans un protocole.

Le client utilisant le protocole doit appeler la fonction sur un type conforme au protocole, n'est-ce pas?

Oui, exactement comme les fonctions d'instance.

Cela rompt l’idée de ne pas avoir à connaître le type conforme au protocole OMI.

Nan. Regardez le code suivant:

protocol Feline {
    var name: String { get }
    static func createRandomFeline() -> Feline
    init()
}

extension Feline {
    static func createRandomFeline() -> Feline {
        return arc4random_uniform(2) > 0 ? Tiger() : Leopard()
    }
}

class Tiger: Feline {
    let name = "Tiger"
    required init() {}
}

class Leopard: Feline {
    let name = "Leopard"
    required init() {}
}

let feline: Feline = arc4random_uniform(2) > 0 ? Tiger() : Leopard()
let anotherFeline = feline.dynamicType.createRandomFeline()

Je ne connais pas le type réel dans la variable feline. Je sais juste que cela est conforme à Feline. Cependant, j'appelle une méthode de protocole statique.

Y a-t-il une meilleure manière de faire cela?

Je vois que vous voudriez appeler une méthode/fonction statique déclarée dans un protocole sans créer une valeur conforme au protocole.

Quelque chose comme ça:

Feline.createRandomFeline() // DANGER: compiler is not happy now

Honnêtement, je ne connais pas la raison pour laquelle ce n'est pas possible.

21
Luca Angeletti

oui c'est possible:

Swift 3

protocol Thing {
  static func genericFunction()
}

//... in another file

var things:[Thing] = []

for thing in things {
  type(of: thing).genericFunction()
}
3
iljn

Merci @appzYourLife pour l'aide! Votre réponse a inspiré ma réponse.

@appzYourLife a répondu à ma question. J'avais un problème sous-jacent que j'essayais de résoudre et le code suivant résout mon problème. Je vais donc l'afficher ici, cela aidera peut-être quelqu'un qui a la même question sous-jacente:

 protocol MyProtocol {
     static func aStaticFunc()
 }

 class SomeClassThatUsesMyProtocolButDoesntConformToIt {

     var myProtocolType: MyProtocol.Type
     init(protocolType: MyProtocol.Type) {
         myProtocolType = protocolType
     }

     func aFunction() {
         myProtocolType.aStaticFunc()
     }
 }
2
SirRupertIII

Un peu tard pour la fête sur celui-ci.

Voici ma solution pour "ajouter" des propriétés/fonctions/types statiques à un protocole utilisant typealias.

Par exemple:

enum PropertyScope {
    case all
    case none
}

struct PropertyNotifications {

    static var propertyDidChange = 
                         Notification.Name("propertyDidChangeNotification")

}

protocol Property {

    typealias Scope = PropertyScope

    typealias Notifications = PropertyNotifications

    var scope: Scope { get set }

}

Ensuite, vous pouvez le faire n'importe où dans votre code:

func postNotification() {
    let scope: Property.Scope = .all

    NotificationCenter.post(name: Property.Notifications.propertyDidChange,
                            object: scope)

}
1
John

J'ai eu une situation où je dois créer le même objet DomainModel à partir de 2 réponse différente. donc cette approche (la méthode static dans protocol m'a aidé) m'a aidée.

protocol BaseResponseKeyList: CodingKey {
   static func getNameKey()->Self
}

enum FirstResponseKeyList: String, BaseResponseKeyList {
    case name

    func getNameKey()->FirstResponseKeyList {
       return .name
    }
}

enum SecondResponseKeyList: String, BaseResponseKeyList {
    case userName

    func getNameKey()->SecondResponseKeyList {
       return .userName
    }
}

struct MyDomainModel<T:BaseResponseKeyList> : Decodable {
    var name:String?

    required init(from d:Decoder) {
       do {
            let container = try d.container(keyedBy:T.self)
            name = try container.decode(String.self, forKey:T.getNameKey())
        }catch(_) {
            print("error")
        }
    }
}

let myDomainModel = try JSONDecoder().decode(MyDomainModel <FirstResponseKeyList>.self, from: data)
let myDomainModel2 = try JSONDecoder().decode(MyDomainModel <SecondResponseKeyList>.self, from: data2)
0
Suryavel TR

L'utilisation de protocoles tels que les interfaces Java est rarement une bonne idée. Ce sont des méta-types, destinés à définir des contrats, ce qui est un genre complètement différent.

Cela étant dit, pour que tout soit clair, je trouve le moyen le plus simple et le plus efficace de créer l'équivalent d'une méthode de fabrique statique d'un protocole pour écrire une fonction libre.

Il devrait contenir le nom du protocole, en espérant que cela évitera les conflits de noms et améliorera la possibilité de découverte.

Dans d'autres langues, createP serait un membre statique de P, nommé create et s'appellerait P.create (...), ce qui améliorerait considérablement la possibilité de découverte et garantirait d'éviter les conflits de noms.

Toutefois, dans Swift, ce n'est pas une option pour les protocoles. Si, pour une raison quelconque, les protocoles sont réellement utilisés pour remplacer les interfaces, au moins, inclure le nom du protocole dans le nom de la fonction est une solution de contournement moche qui reste légèrement meilleure que rien.

P.S. si l'objectif est réellement de réaliser quelque chose comme une hiérarchie d'héritage avec des structures, les énumérations de style d'union sont l'outil qui est conçu pour atteindre cet objectif :)

protocol P
{
    var x: Int { get }
}

func createP() -> P
{
    if (todayIsMonday())
    {
        return A()
    }
    else
    {
        return B()
    }
}

class A: P
{
    var x = 5
}

class B: P
{
    var x = 7
}
0
yeoman

Ce n'est pas une réponse, c'est une extension de la question. Dis que j'ai:

@objc public protocol InteractivelyNameable: Nameable {

    static func alertViewForNaming(completion:@escaping((_ success: Bool, _ didCancel: Bool, _ error: Error?) -> Void)) -> UIAlertController?
}

Et j'ai un contrôleur de vue générique qui gère divers types (le type générique est .fetchableObjectType ... essentiellement NSFetchResult). Je dois vérifier si un type d'objet spécifique est conforme au protocole et, le cas échéant, l'invoquer.

quelque chose comme:

    // valid Swift code
    if self.dataSource.fetchableObjectType is InteractivelyNameable {

        // not valid Swift code
        if let alert = (self.dataSource.fetchableObjectType as InteractivelyNameable).alertViewForNaming(....)
    }
0
horseshoe7