Het delegatiepatroon is een van de meest voorkomende patronen in iOS- en OS X-ontwikkeling. Het is een eenvoudig patroon dat sterk wordt gebruikt door de kaders van Apple en zelfs de eenvoudigste iOS-applicatie maakt gebruik van delegatie om zijn werk te doen. Laten we beginnen met te kijken naar de definitie van delegatie.
De definitie van het delegatiepatroon is kort en eenvoudig. Dit is hoe Apple het patroon definieert.
Een gedelegeerde is een object dat optreedt namens of in coördinatie met een ander object wanneer dat object een gebeurtenis in een programma tegenkomt.
Laten we dat onderbreken. Het delegatiepatroon omvat twee objecten, de gedelegeerde en het delegerende object. De UITableView
klasse definieert bijvoorbeeld a delegeren
eigendom waaraan het evenementen delegeert. De eigenschap van de gedelegeerde moet voldoen aan de UITableViewDelegate
protocol, dat is gedefinieerd in het headerbestand van de UITableView
klasse.
In dit voorbeeld is de tabelweergaveinstantie het delegerende object. De gedelegeerde is meestal een view-controller, maar het kan elk object zijn dat voldoet aan de UITableViewDelegate
protocol. Als u niet vertrouwd bent met protocollen, voldoet een klasse aan een protocol als het de vereiste methoden van het protocol implementeert. We zullen een beetje later naar een voorbeeld kijken.
Wanneer de gebruiker op een rij tikt in de tabelweergave, meldt de tabelweergave zijn gemachtigde door deze een bericht te sturen tableView (_: didSelectRowAtIndexPath :)
. Het eerste argument van deze methode is de tabelweergave die het bericht verzendt. Het tweede argument is het indexpad van de rij waarop de gebruiker tikte.
De tabelweergave geeft alleen zijn afgevaardigde van deze gebeurtenis op de hoogte. Het is aan de afgevaardigde om te beslissen wat er moet gebeuren wanneer een dergelijke gebeurtenis zich voordoet. Deze scheiding van verantwoordelijkheden, zoals u in een oogwenk zult leren, is een van de belangrijkste voordelen van het delegatiepatroon.
Delegatie heeft verschillende voordelen, de eerste is herbruikbaarheid. Omdat de tabelweergave gebruikersinteractie delegeert aan zijn gemachtigde, hoeft de tabelweergave niet te weten wat er moet gebeuren wanneer een van de rijen wordt aangeboord.
Anders gezegd, de tabelweergave kan onwetend blijven van de implementatiedetails van hoe de gebruikersinteractie wordt afgehandeld door de toepassing. Deze verantwoordelijkheid wordt gedelegeerd aan de afgevaardigde, bijvoorbeeld een view-controller.
Het directe voordeel is dat de UITableView
klasse kan worden gebruikt zoals in de meeste situaties. Meestal is subklasse niet nodig UITableView
om het aan te passen aan de behoeften van uw toepassing.
Een ander belangrijk voordeel van delegeren is losse koppeling. In mijn artikel over singletons benadruk ik dat een strakke koppeling zoveel mogelijk moet worden vermeden. Delegatie is een ontwerppatroon dat actief losse koppeling bevordert. Wat bedoel ik daarmee?
De UITableView
klas is gekoppeld aan zijn afgevaardigde om zijn werk te doen. Als er geen deelnemer aan de tabelweergave is gekoppeld, kan de tabelweergave de gebruikersinteractie niet verwerken of erop reageren. Dit betekent dat er een bepaald niveau van koppeling moet zijn. De tabelweergave en zijn deelnemer zijn echter los gekoppeld, omdat elke klasse die de. Implementeert UITableViewDelegate
protocol kan fungeren als de gedelegeerde van de tabelweergave. Het resultaat is een flexibele en losjes gekoppelde objectgrafiek.
Een minder bekend voordeel van delegatie is de scheiding van verantwoordelijkheden. Wanneer u een objectgrafiek maakt, is het belangrijk om te weten welke objecten verantwoordelijk zijn voor welke taken. Het delegatiepatroon maakt dit heel duidelijk.
In het geval van de UITableView
klasse, de gedelegeerde van de tabelweergave is verantwoordelijk voor de omgang met gebruikersinteractie. De tabelweergave zelf is verantwoordelijk voor het detecteren van gebruikersinteractie. Dit is een duidelijke scheiding van verantwoordelijkheden. Zo'n scheiding maakt je baan als ontwikkelaar veel eenvoudiger en duidelijker.
Er zijn een paar smaken van het delegatiepatroon. Laten we doorgaan met het verder verkennen van de UITableViewDelegate
protocol.
De UITableViewDelegate
protocol moet worden geïmplementeerd door de gedelegeerde van de tabelweergave. De tabelweergave meldt zijn gemachtigde via de UITableViewDelegate
protocol over gebruikersinteractie, maar het gebruikt ook de gedelegeerde voor de lay-out.
Een belangrijk verschil tussen Swift en Objective-C is de mogelijkheid om protocolmethoden als optioneel te markeren. In Objective-C zijn de methoden van een protocol standaard vereist. De methoden van de UITableViewDelegate
protocol zijn echter optioneel. Met andere woorden, het is mogelijk dat een klasse zich conformeert aan de UITableViewDelegate
protocol zonder de methoden van het protocol te implementeren.
In Swift is echter een klasse vereist die voldoet aan een bepaald protocol om elke methode die door het protocol wordt gedefinieerd, te implementeren. Dit is veel veiliger omdat het delegerende object niet hoeft te verifiëren of de gedelegeerde een protocolmethode implementeert. Dit subtiele, maar belangrijke verschil wordt verderop in deze tutorial geïllustreerd wanneer we het delegatiepatroon implementeren.
Er is een ander patroon dat nauw verwant is aan het delegatiepatroon, de gegevensbronpatroon. De UITableViewDataSource
protocol is een voorbeeld van dit patroon. De UITableView
klasse onthult een databron
eigenschap die van het type is UITableViewDataSource
(ID kaart
in Objective-C). Dit betekent dat de gegevensbron van de tabelweergave elk object kan zijn dat de UITableViewDataSource
protocol.
Het gegevensbronobject is verantwoordelijk voor het beheer van de gegevensbron van het object waarvan het de gegevensbron is. Het is belangrijk om op te merken dat het gegevensbronobject verantwoordelijk is voor het bijhouden van een verwijzing naar de items die worden blootgesteld aan het doelobject, zoals een tabelweergave of een verzamelweergave.
Een tabelweergave vraagt bijvoorbeeld zijn gegevensbron om de gegevens die hij moet weergeven. Het tabelaanzicht is niet verantwoordelijk voor het bijhouden van de gegevensobjecten die het moet weergeven. Die rol wordt overgedragen aan het gegevensbronobject.
Het gegevensbronpatroon past mooi in de Model-View-Controller of MVC patroon. Waarom is dat? Een tabelweergave maakt bijvoorbeeld deel uit van de weergavelaag. Het hoeft niet te weten van de modellaag en moet dit niet doen en is niet verantwoordelijk voor het verwerken van de gegevens die afkomstig zijn van de modellaag. Dit impliceert dat de gegevensbron van een tabelweergave of een andere weergavecomponent die het gegevensbronpatroon implementeert, vaak een of andere controller is. Op iOS is dit meestal een UIViewController
subklasse.
De methodehandtekeningen van een gegevensbronprotocol volgen hetzelfde patroon als die van een gedelegeerd protocol. Het object dat de berichten naar de gegevensbron stuurt, wordt doorgegeven als het eerste argument. Het gegevensbronprotocol moet alleen methoden definiëren die betrekking hebben op de gegevens die door het verzoekende object worden gebruikt.
In een tabelweergave wordt bijvoorbeeld de gegevensbron gevraagd voor het aantal secties en rijen dat moet worden weergegeven. Maar het meldt ook de gegevensbron dat een rij of sectie is ingevoegd of verwijderd. Dit laatste is belangrijk omdat de gegevensbron zichzelf moet bijwerken om de wijzigingen weer te geven die zichtbaar zijn in de tabelweergave. Als het tafelbeeld en de gegevensbron niet synchroon lopen, gebeuren er slechte dingen.
Het implementeren van het gedelegeerde patroon is vrij eenvoudig nu we begrijpen hoe het werkt. Bekijk het volgende Objective-C-voorbeeld.
#importeren@protocol AddItemViewControllerDelegate; @interface AddItemViewController: UIViewController @property (weak, nonatomic) id delegeren; @end @protocol AddItemViewControllerDelegate - (void) viewControllerDidCancel: (AddItemViewController *) viewController; - (void) viewController: (AddItemViewController *) viewController didAddItem: (NSString *) item; @optional - (BOOL) viewController: (AddItemViewController *) viewController validateItem: (NSString *) item; @einde
We verklaren een klas, AddItemViewController
, welke zich uitstrekt UIViewController
. De klasse verklaart een eigendom, delegeren
, van type ID kaart
. Merk op dat de eigenschap is gemarkeerd als zwak, wat betekent dat een AddItemViewController
instantie houdt een zwakke verwijzing naar zijn afgevaardigde.
Merk ook op dat ik een forward protocolverklaring heb toegevoegd onder de importinstructie van het UIKit-framework. Dit is nodig om een waarschuwing voor compileren te voorkomen. We zouden de protocolverklaring onder de importinstructie kunnen verplaatsen, maar ik geef er de voorkeur aan deze onder de klasse-interface te plaatsen. Dit is niets meer dan een persoonlijke voorkeur.
De protocolverklaring is ook vrij eenvoudig. De AddItemViewControllerDelegate
protocol breidt het NSObject
protocol. Dit is niet verplicht, maar het zal zeer nuttig blijken te zijn. We zullen ontdekken waarom dat een beetje later is.
De AddItemViewControllerDelegate
protocol verklaart twee vereiste methoden en één optionele methode. Zoals ik eerder al zei, is het een goede gewoonte om het delegerende object als de eerste parameter van elke gedelegeerde methode door te geven om de gedelegeerde te laten weten welk object het bericht verzendt.
De vereiste methoden brengen de deelnemer op de hoogte van een gebeurtenis, een annulering of een toevoeging. De optionele methode vraagt de gedelegeerde om feedback. Het verwacht dat de afgevaardigde terugkomt JA
of NEE
.
Dit is het eerste deel van de delegatiepuzzel. We hebben een klasse opgegeven die a verklaart delegeren
eigendom en we hebben een gedelegeerd protocol gedeclareerd. Het tweede stukje van de puzzel roept de gedelegeerde methoden in de AddItemViewController
klasse. Laten we kijken hoe dat werkt.
Bij de implementatie van de AddItemViewController
klasse, implementeren we een annuleren:
actie. Deze actie kan worden gekoppeld aan een knop in de gebruikersinterface. Als de gebruiker op de knop tikt, wordt de afgevaardigde op de hoogte gesteld van deze gebeurtenis en als gevolg hiervan kan de afgevaardigde de afspraak afwijzen AddItemViewController
aanleg.
- (IBAction) cancel: (id) afzender if (self.delegate && [self.delegate respondsToSelector: @selector (viewControllerDidCancel :)]) [self.delegate viewControllerDidCancel: self];
Het wordt aanbevolen om te controleren of het delegatieobject dat niet is nul
en dat het de gedelegeerde methode implementeert die we op het punt staan aan te roepen, viewControllerDidCancel:
. Dit is gemakkelijk dankzij de respondsToSelector:
methode, gedeclareerd in de NSObject
protocol. Dit is de reden waarom de AddItemViewControllerDelegate
protocol breidt het NSObject
protocol. Door het uitbreiden van de NSObject
protocol krijgen we deze functionaliteit gratis.
U kunt de controle voor de eigenschap van de gedelegeerde weglaten nul
, sinds respondsToSelector:
zal terugkeren nul
als de gedelegeerde eigenschap is nul
. Ik voeg deze controle meestal toe omdat deze duidelijk laat zien wat we testen.
Het derde en laatste stukje van de puzzel is de implementatie van het gedelegeerde protocol door het gedelegeerde object. Het volgende codefragment geeft het maken van een weer AddItemViewController
voorbeeld en de implementatie van een van de gedelegeerde methoden.
- (IBAction) addItem: (id) afzender // Initialize View Controller AddItemViewController * viewController = [[AddItemViewController alloc] init]; // Configureer View Controller [viewController setDelegate: self]; // Present View Controller [self presentViewController: viewController animated: YES completion: nil];
- (void) viewControllerDidCancel: (AddItemViewController *) viewController // Controller regel toevoegen beëindigen ...
Vergeet niet om te voldoen aan de klasse die optreedt als de afgevaardigde naar de AddItemViewControllerDelegate
protocol zoals hieronder getoond. U kunt dit toevoegen in de klasse-interface of in een privéklasse-extensie.
#import "AddItemViewController.h" @interface ViewController ()@einde
In Swift is het delegatiepatroon net zo eenvoudig uit te voeren en je zult merken dat Swift de delegatie iets eleganter maakt. Laten we het bovenstaande voorbeeld in Swift implementeren. Dit is wat de AddItemViewController
klasse ziet eruit als in Swift.
import UIKit-protocol AddItemViewControllerDelegate: NSObjectProtocol func viewControllerDidCancel (viewController: AddItemViewController) func viewController (viewController: AddItemViewController, didAddItem: String) func viewController (viewController: AddItemViewController, validateItem: String) -> Bool class AddItemViewController: UIViewController var delegate: AddItemViewControllerDelegate? func cancel (verzender: AnyObject) delegate? .viewControllerDidCancel (self)
De protocolverklaring ziet er in Swift een beetje anders uit. Merk op dat de AddItemViewControllerDelegate
protocol breidt het NSObjectProtocol
in plaats van de NSObject
protocol. In Swift kunnen klassen en protocollen niet dezelfde naam hebben, vandaar de NSObject
protocol wordt in Swift anders genoemd.
De delegeren
eigenschap is een variabele van het type AddItemViewControllerDelegate?
. Let op het vraagteken aan het einde van de protocolnaam. De eigenschap gedelegeerde is een optioneel.
In de annuleren(_:)
methode, roepen we de viewControllerDidCancel (_ :)
gedelegeerde methode. Die ene regel laat zien hoe elegant Swift kan zijn. We pakken het veilig uit delegeren
eigenschap voordat de gedelegeerde methode wordt aangeroepen. Het is niet nodig om te controleren of de gedelegeerde de viewControllerDidCancel (_ :)
methode omdat elke methode van een protocol vereist is in Swift.
Laten we nu kijken naar de ViewController
klasse, die het AddItemViewControllerDelegate
protocol. De interface laat ons zien dat de ViewController
class breidt het uit UIViewController
klasse en keurt het goed AddItemViewControllerDelegate
protocol.
import UIKit class ViewController: UIViewController, AddItemViewControllerDelegate func addItem (send: AnyObject) // Initialize View controller laat viewController = AddItemViewController () // Configureer View Controller viewController.delegate = self // Huidige weergave Controller presentViewController (viewController, geanimeerd: true , completion: nil) func viewControllerDidCancel (viewController: AddItemViewController) // Stuurregelitem weergeven negeren ... func viewController (viewController: AddItemViewController, didAddItem: String) func viewController (viewController: AddItemViewController, validateItem: String) -> Bool
In de Voeg item toe(_:)
methode, initialiseren we een exemplaar van de AddItemViewController
klasse, stel het in delegeren
eigendom en presenteer het aan de gebruiker. Merk op dat we elke gedelegeerde methode van de AddItemViewControllerDelegate
protocol. Als we dat niet doen, zal de compiler ons vertellen dat het ViewController
klasse voldoet niet aan de AddItemViewControllerDelegate
protocol. Probeer dit uit door een van de gedelegeerde methoden te becommentariëren.
Delegatie is een patroon dat u vaak tegenkomt bij het ontwikkelen van iOS- en OS X-applicaties. Cocoa is sterk afhankelijk van dit ontwerppatroon, dus het is belangrijk om ermee vertrouwd te raken.
Sinds de introductie van blokken, een paar jaar geleden, heeft Apple langzaam een alternatieve op blokken gebaseerde API aangeboden aan een aantal delegatie-implementaties. Sommige ontwikkelaars hebben de leiding van Apple gevolgd door hun eigen op blokken gebaseerde alternatieven aan te bieden. De populaire AFNetworking-bibliotheek is bijvoorbeeld sterk afhankelijk van blokken in plaats van overdracht, wat resulteert in een elegante, intuïtieve API.