Dans Swift, je peux définir explicitement le type d'une variable en la déclarant comme suit:
var object: TYPE_NAME
Si nous voulons aller plus loin et déclarer une variable conforme à plusieurs protocoles, nous pouvons utiliser le protocol
déclaratif:
var object: protocol<ProtocolOne,ProtocolTwo>//etc
Que faire si je souhaite déclarer un objet conforme à un ou plusieurs protocoles et qui est également d'un type de classe de base spécifique? L'équivalent Objective-C ressemblerait à ceci:
NSSomething<ABCProtocolOne,ABCProtocolTwo> * object = ...;
Dans Swift, je m'attendrais à ce que cela ressemble à ceci:
var object: TYPE_NAME,ProtocolOne//etc
Cela nous donne la flexibilité de pouvoir gérer l'implémentation du type de base ainsi que l'interface ajoutée définie dans le protocole.
Y a-t-il un autre moyen plus évident que je pourrais manquer?
Exemple
À titre d'exemple, disons que j'ai une UITableViewCell
usine qui est chargée de renvoyer des cellules conformes à un protocole. Nous pouvons facilement configurer une fonction générique qui retourne des cellules conformes à un protocole:
class CellFactory {
class func createCellForItem<T: UITableViewCell where T:MyProtocol >(item: SpecialItem,tableView: UITableView) -> T {
//etc
}
}
plus tard, je veux retirer ces cellules de la file d'attente tout en exploitant à la fois le type et le protocole
var cell: MyProtocol = CellFactory.createCellForItem(somethingAtIndexPath) as UITableViewCell
Cela renvoie une erreur car une cellule de vue tableau n'est pas conforme au protocole ...
Je voudrais pouvoir spécifier que la cellule est a UITableViewCell
et est conforme à la MyProtocol
dans la déclaration de variable?
Justification
Si vous êtes familier avec le modèle d'usine, cela aurait du sens dans le contexte de pouvoir renvoyer des objets d'une classe particulière qui implémentent une certaine interface.
Tout comme dans mon exemple, nous aimons parfois définir des interfaces qui ont du sens lorsqu'elles sont appliquées à un objet particulier. Mon exemple de cellule de vue tableau est une de ces justifications.
Bien que le type fourni ne soit pas exactement conforme à l'interface mentionnée, l'objet renvoyé par l'usine le fait et j'aimerais donc avoir la flexibilité d'interagir avec le type de classe de base et l'interface de protocole déclarée
la source
NSSomething<ABCProtocolOne,ABCProtocolTwo> * object = ...;
. Cet objet semble tout à fait inutile car ilNSSomething
sait déjà à quoi il se conforme. S'il n'est pas conforme à l'un des protocoles de,<>
vous obtiendrez desunrecognised selector ...
plantages. Cela n'offre aucune sécurité de type.Réponses:
Dans Swift 4, il est maintenant possible de déclarer une variable qui est une sous-classe d'un type et implémente un ou plusieurs protocoles en même temps.
Pour faire une variable facultative:
ou comme paramètre d'une méthode:
Apple l'a annoncé à la WWDC 2017 lors de la session 402: Quoi de neuf dans Swift
la source
Vous ne pouvez pas déclarer de variable comme
ni déclarer le type de retour de fonction comme
Vous pouvez déclarer comme paramètre de fonction comme celui-ci, mais il s'agit essentiellement d'une conversion ascendante.
À partir de maintenant, tout ce que vous pouvez faire est de:
Avec cela, techniquement
cell
est identique àasProtocol
.Mais, comme pour le compilateur,
cell
n'aUITableViewCell
qu'une interface , tandis queasProtocol
n'a qu'une interface de protocoles. Ainsi, lorsque vous souhaitez appelerUITableViewCell
les méthodes de, vous devez utilisercell
variable. Lorsque vous souhaitez appeler la méthode des protocoles, utilisezasProtocol
variable.Si vous êtes sûr que la cellule est conforme aux protocoles, vous n'avez pas à utiliser
if let ... as? ... {}
. comme:la source
-> UITableViewCell<MyProtocol>
, ce n'est pas valide, car ceUITableViewCell
n'est pas un type générique. Je pense que cela ne compile même pas.cell
il n'y a que des méthodes de protocoles (pour le compilateur).Malheureusement, Swift ne prend pas en charge la conformité du protocole au niveau objet. Cependant, il existe une solution de contournement quelque peu délicate qui peut servir vos objectifs.
Ensuite, partout où vous avez besoin de faire quoi que ce soit que possède UIViewController, vous accéderez à l'aspect .viewController de la structure et tout ce dont vous avez besoin de l'aspect protocole, vous référeriez au .protocol.
Par exemple:
Désormais, chaque fois que vous avez besoin de mySpecialViewController pour faire quoi que ce soit lié à UIViewController, vous faites simplement référence à mySpecialViewController.viewController et chaque fois que vous en avez besoin pour effectuer une fonction de protocole, vous référencez mySpecialViewController.protocol.
Espérons que Swift 4 nous permettra de déclarer un objet avec des protocoles qui lui sont attachés à l'avenir. Mais pour l'instant, cela fonctionne.
J'espère que cela t'aides!
la source
Peut-être que je me trompe, mais ne parlez-vous pas d'ajouter la conformité de protocole à la
UITableCellView
classe? Le protocole est dans ce cas étendu à la classe de base, et non à l'objet. Consultez la documentation d'Apple sur déclaration d'adoption de protocole avec une extension qui, dans votre cas, serait quelque chose comme:En plus de la documentation Swift déjà référencée, consultez également l'article de Nate Cooks Fonctions génériques pour les types incompatibles avec d'autres exemples.
L'adoption du protocole fera exactement cela, faire adhérer un objet au protocole donné. Soyez cependant conscient du côté négatif, qu'une variable d'un type de protocole donné ne savoir quoi que ce soit en dehors du protocole. Mais cela peut être contourné en définissant un protocole contenant toutes les méthodes / variables / ...
Si vous souhaitez qu'une méthode générique, une variable se conforme à la fois à un protocole et à des types de classe de base, vous pourriez être malchanceux. Mais il semble que vous deviez définir le protocole suffisamment large pour avoir les méthodes de conformité nécessaires, et en même temps assez étroit pour avoir la possibilité de l'adopter aux classes de base sans trop de travail (c'est-à-dire simplement déclarer qu'une classe est conforme au protocole).
la source
Une fois, j'ai eu une situation similaire en essayant de lier mes connexions d'interacteurs génériques dans Storyboards (IB ne vous permettra pas de connecter des prises à des protocoles, uniquement des instances d'objet), que j'ai contournée en masquant simplement la classe de base public ivar avec un calcul privé propriété. Bien que cela n'empêche pas quelqu'un d'effectuer des affectations illégales en soi, cela constitue un moyen pratique d'empêcher en toute sécurité toute interaction indésirable avec une instance non conforme au moment de l'exécution. (c'est-à-dire éviter d'appeler des méthodes déléguées à des objets qui ne sont pas conformes au protocole.)
Exemple:
Le "outputReceiver" est déclaré facultatif, tout comme le "protocolOutputReceiver" privé. En accédant toujours au outputReceiver (aka délégué) via ce dernier (la propriété calculée), je filtre efficacement tous les objets qui ne sont pas conformes au protocole. Maintenant, je peux simplement utiliser le chaînage facultatif pour appeler en toute sécurité l'objet délégué, qu'il implémente ou non le protocole ou même qu'il existe.
Pour appliquer cela à votre situation, vous pouvez faire en sorte que l'ivar public soit de type "YourBaseClass?" (par opposition à AnyObject) et utilisez la propriété privée calculée pour appliquer la conformité du protocole. FWIW.
la source