[Ios] Génériques dans Swift - "Le paramètre générique 'T' n'a pas pu être déduit


Answers

Dans une méthode comme celle-ci, renvoyer T signifie que vous devez retourner T Si vous renvoyez MyViewController , le type de retour doit être MyViewController . T est un type générique qui prendra la forme de tout ce que le compilateur Swift peut en déduire.

Ainsi, avec la signature de votre méthode, une implémentation simple du protocole et de la méthode pourrait ressembler à ceci.

protocol MyProtocol {
    var name: String { get set }
}

func myMethod<T where T : UIViewController, T : MyProtocol>() -> T {
    var vc = T()
    vc.name = "Hello, world"
    return vc
}

Donc, compte tenu de votre exemple d'utilisation:

let x = myMethod()

Comment le compilateur pourrait-il savoir quel est le type concret de T ? Il n'y a rien qui lui donne un soupçon de MyViewController . La seule chose que nous savons est que quel que soit T , il devrait être MyViewController ou une sous-classe de celui-ci. Et il devrait se conformer à MyProtocol . Mais cela ne fournit pas d'informations sur ce que devrait être le type de T

Le seul endroit où le compilateur peut déduire ce que nous voulons que T soit à travers la valeur de retour. Tout le code entre <> sont des contraintes pour ce que T est autorisé à être. -> T est le seul endroit où T est vu en dehors des contraintes. Donc, si nous pouvons en quelque sorte dire au compilateur ce que nous voulons que myMethod renvoie, nous lui avons donné suffisamment d'informations pour en déduire T

Votre typecast fonctionne mais je suis d'accord que ce n'est pas très joli. Une façon beaucoup plus jolie pour le compilateur d'inférer T est la suivante.

let vc: MyViewController = myMethod()

En spécifiant le type de vc , le compilateur comprend que nous voulons que myMethod renvoie un MyViewController . Donc maintenant le type de T peut être déduit et si nous retournons T , nous retournons MyViewController .

Question

Je voudrais retourner un UIViewController conforme à MyProtocol partir d'une méthode, donc j'utilise la signature de la méthode:

func myMethod<T where T : UIViewController, T : MyProtocol>() -> T {

Première chose que je ne comprends pas: si myMethod renvoie par exemple un MyViewController qui doit suivre la signature, je dois forcer le lancer:

class MyViewController: UIViewController, MyProtocol

Je ne peux pas simplement return MyViewController() mais j'ai besoin de le lancer comme ceci: return MyViewController() as! T return MyViewController() as! T - pourquoi est-ce nécessaire?

Et la deuxième chose: comment puis-je utiliser cette méthode quelque part? Je ne peux pas simplement dire

let x = myMethod() as? UIViewController

comme je reçois l'erreur

Generic parameter 'T' could not be inferred

Comment puis-je réaliser quelque chose comme ça? Si je le lance sur MyViewController cela fonctionne, mais je voudrais éviter cela bien sûr.

EDIT: Exemple

class MyViewController : UIViewController, MyProtocol {
}

protocol MyProtocol {
}

func myMethod<T>() -> T where T : UIViewController, T : MyProtocol {
    return MyViewController() as! T // why is the cast necessary?
}

ok, je reçois une partie, mais pourquoi la distribution à T nécessaire? MyViewController est une sous-classe de UIViewController et est conforme au protocole, donc aucun casting ne devrait être nécessaire, non?