Modelul de delegare se numără printre cele mai comune modele în dezvoltarea iOS și OS X. Este un model simplu, care este puternic folosit de cadrele Apple și chiar și cea mai simplă aplicație iOS se ocupă de delegarea de a-și face munca. Să începem prin examinarea definiției delegației.
Definirea modelului de delegare este scurtă și simplă. Acesta este modul în care Apple definește modelul.
Un delegat este un obiect care acționează în numele sau în coordonare cu un alt obiect atunci când acel obiect întâlnește un eveniment într-un program.
Să lăsăm asta jos. Modelul de delegare implică două obiecte, delegatul și obiectul delegare. UITableView
clasa, de exemplu, definește a delega
proprietate la care deleagă evenimente. Proprietatea delegaților trebuie să respecte cerințele UITableViewDelegate
protocol, care este definit în fișierul antet al UITableView
clasă.
În acest exemplu, instanța de vizualizare a tabelului este obiectul delegare. Delegatul este, de obicei, un controler de vizualizare, dar poate fi orice obiect care se conformează UITableViewDelegate
protocol. Dacă nu sunteți familiarizați cu protocoalele, o clasă se conformează unui protocol dacă implementează metodele necesare ale protocolului. O să vedem un exemplu mai târziu.
Când utilizatorul fixează un rând în vizualizarea de tabelă, vizualizarea tabelului îi notifică delegatul prin trimiterea unui mesaj tableView (_: didSelectRowAtIndexPath :)
. Primul argument al acestei metode este afișarea în tabel a mesajului. Al doilea argument este calea index a rândului pe care utilizatorul a dat-o.
Vizualizarea tabelului îi notifică doar delegatul despre acest eveniment. Depinde de delegat să decidă ce trebuie să se întâmple atunci când a avut loc un astfel de eveniment. Această separare a responsabilităților, după cum veți învăța într-un moment, este unul dintre beneficiile cheie ale modelului de delegare.
Delegația are mai multe avantaje, prima fiind reutilizarea. Deoarece vizualizarea tabelului deleagă interacțiunea utilizatorului cu delegatul său, vizualizarea de tabel nu trebuie să știe ce trebuie să se întâmple atunci când unul dintre rândurile sale este selectat.
Altfel, vizualizarea tabelului poate rămâne ignorantă în detaliile implementării modului în care interacțiunea cu utilizatorul este gestionată de aplicație. Această responsabilitate este delegată delegatului, de exemplu un controler de vizualizare.
Beneficiul direct este că UITableView
clasa poate fi utilizată ca în cele mai multe situații. De cele mai multe ori, nu este nevoie de subclase UITableView
pentru a vă adapta la nevoile aplicației dvs..
Un alt avantaj important al delegării este cuplajul liber. În articolul meu despre singletoni, subliniez că cuplarea strânsă ar trebui evitată cât mai mult posibil. Delegația este un model de design care promovează în mod activ cuplarea liberă. Ce vreau să spun prin asta?
UITableView
clasa este cuplată cu delegatul său pentru a-și desfășura activitatea. Dacă niciun delegat nu este asociat cu vizualizarea de tabelă, vizualizarea tabelului nu poate gestiona sau nu răspunde la interacțiunea utilizatorului. Aceasta înseamnă că trebuie să existe un anumit nivel de cuplare. Vederea tabelului și delegatul său sunt totuși cuplate, pentru că fiecare clasă care implementează UITableViewDelegate
protocolul poate acționa în calitate de delegat al mesei de vedere. Rezultatul este un graf obiect flexibil și slab cuplat.
Un avantaj mai puțin cunoscut al delegării este separarea responsabilităților. Ori de câte ori creați un grafic de obiecte, este important să știți care obiecte sunt responsabile pentru ce sarcini. Modelul de delegare face acest lucru foarte clar.
În cazul UITableView
clasa, delegatul vizualizării de tabel este responsabil pentru manipularea interacțiunii utilizatorilor. Vizualizarea tabelului în sine este responsabilă pentru detectarea interacțiunii utilizatorilor. Aceasta este o separare clară a responsabilităților. O astfel de separare face munca ta ca dezvoltator mult mai ușoară și mai clară.
Există câteva arome ale modelului de delegare. Să continuăm explorând în continuare UITableViewDelegate
protocol.
UITableViewDelegate
protocolul trebuie să fie implementat de către delegatul din tabelul de vedere al mesei. Vizualizarea tabelului îi notifică delegatul prin intermediul UITableViewDelegate
protocol privind interacțiunea cu utilizatorul, dar utilizează, de asemenea, delegatul pentru aspectul acestuia.
O diferență importantă între Swift și Obiectiv-C este posibilitatea de a marca metodele protocolului ca opționale. În Obiectiv-C, metodele unui protocol sunt necesare în mod implicit. Metodele UITableViewDelegate
Protocolul, cu toate acestea, este opțional. Cu alte cuvinte, este posibil ca o clasă să se conformeze UITableViewDelegate
fără a aplica niciuna dintre metodele protocolului.
Cu toate acestea, în Swift este necesară o clasă conformă cu un anumit protocol pentru a implementa fiecare metodă definită de protocol. Acest lucru este mult mai sigur, deoarece obiectul de delegare nu trebuie să verifice dacă delegatul implementează o metodă de protocol. Această diferență subtilă, dar importantă este ilustrată mai târziu în acest tutorial când implementăm modelul de delegare.
Există un alt model care este strâns legat de modelul de delegare sursa de date. UITableViewDataSource
protocol este un exemplu al acestui model. UITableView
clasa expune a sursă de date
proprietate care este de tip UITableViewDataSource
(id
în obiectivul C). Aceasta înseamnă că sursa de date a tabelului poate fi orice obiect care implementează UITableViewDataSource
protocol.
Obiectul sursă de date este responsabil pentru gestionarea sursei de date a obiectului din care este sursa de date. Este important să rețineți că obiectul sursă de date este responsabil pentru păstrarea unei trimiteri la elementele pe care le expune la obiectul țintă, cum ar fi o vizualizare de tabelă sau o colecție.
O vizualizare tabelă, de exemplu, întreabă sursa de date pentru datele pe care trebuie să le afișeze. Vizualizarea tabelului nu este responsabilă pentru menținerea apăsării obiectelor de date de care are nevoie pentru afișare. Acest rol este predat obiectului sursă de date.
Modelul sursei de date se potrivește frumos în Model-View-Controller sau MVC model. De ce este asta? O vizualizare de tabelă, de exemplu, face parte din stratul de vizualizare. Nu știe și nu trebuie să știe despre stratul de model și nu este responsabil cu manipularea datelor care provin din stratul modelului. Aceasta implică faptul că sursa de date a unei vizualizări de tabel sau orice altă componentă de vizualizare care implementează modelul de sursă de date este adesea un controler de un anumit tip. Pe iOS, este de obicei a UIViewController
subclasă.
Semnătura metodei unui protocol de sursă de date respectă același model ca și protocolul unui delegat. Obiectul care trimite mesajele către sursa de date este trecut ca primul argument. Protocolul sursă de date trebuie să definească numai metode care se referă la datele care sunt utilizate de obiectul solicitant.
O vizualizare tabelă, de exemplu, întreabă sursa de date pentru numărul de secțiuni și rânduri pe care ar trebui să le afișeze. Dar, de asemenea, notifică sursa de date că un rând sau o secțiune a fost inserat sau șters. Acesta din urmă este important, deoarece sursa de date trebuie să se actualizeze pentru a reflecta modificările vizibile în vizualizarea tabelului. Dacă vizualizarea tabelului și sursa de date nu se sincronizează, se întâmplă lucruri rele.
Implementarea modelului de delegat este destul de simplă acum, când înțelegem cum funcționează. Uitați-vă la următorul exemplu de Obiectiv-C.
#import@protocol AddItemViewControllerDelegate; @interface AddItemViewController: UIViewController @property (slab, nonatomic) id delega; @end @ protocol AddItemViewControllerDelegate - (void) viewControllerDidCancel: (AddItemViewController *) viewController; - (void) viewController: (AddItemViewController *) vizualizareController didAddItem: (NSString *) item; @optional - (BOOL) viewController: (AddItemViewController *) vizualizareController validateItem: (NSString *) element; @Sfârșit
Declarăm o clasă, AddItemViewController
, care se extinde UIViewController
. Clasa declară o proprietate, delega
, de tip id
. Rețineți că proprietatea este marcată ca slab, ceea ce înseamnă că o AddItemViewController
instanța păstrează o referință slabă la delegatul său.
De asemenea, rețineți că am adăugat o declarație de protocol înaintea declarației de import a cadrului UIKit. Acest lucru este necesar pentru a evita avertizarea compilatorului. Am putea muta declarația de protocol sub declarația de import, dar prefer să o pun sub interfața de clasă. Aceasta nu este altceva decât o preferință personală.
Declarația protocolului este, de asemenea, destul de simplă. AddItemViewControllerDelegate
protocol extinde NSObject
protocol. Acest lucru nu este obligatoriu, dar se va dovedi foarte util. Vom afla de ce un pic mai târziu.
AddItemViewControllerDelegate
protocol declară două metode necesare și o metodă opțională. Așa cum am menționat mai devreme, este o practică bună să treci obiectul de delegare ca primul parametru al fiecărei metode de delegat pentru a informa delegatul care obiect trimite mesajul.
Metodele solicitate notifică delegatul despre un eveniment, o anulare sau o adăugare. Metoda opțională cere delegatului feedback. Se așteaptă ca delegatul să se întoarcă DA
sau NU
.
Aceasta este prima piesă a puzzle-ului delegației. Am declarat o clasă care declară a delega
proprietate și am declarat un protocol delegat. A doua piesă a puzzle-ului invocă metodele delegate în AddItemViewController
clasă. Să vedem cum funcționează.
În punerea în aplicare a directivei AddItemViewController
clasa, punem în aplicare a Anulare:
acțiune. Această acțiune ar putea fi conectată la un buton din interfața cu utilizatorul. Dacă utilizatorul fixează butonul, delegatul este informat despre acest eveniment și, în consecință, delegatul îl poate respinge AddItemViewController
instanță.
- (IBAction) anulați: (id) expeditorul if (self.delegate && [self.delegate respondsToSelector: @selector (viewControllerDidCancel :)]) [auto.delegate viewControllerDidCancel: self];
Se recomandă să verificați dacă obiectul delegat nu este zero
și că implementează metoda delegat pe care suntem pe cale să o invocăm, viewControllerDidCancel:
. Acest lucru este ușor datorită respondsToSelector:
metoda, declarată în NSObject
protocol. Acesta este motivul pentru care AddItemViewControllerDelegate
protocol extinde NSObject
protocol. Prin extinderea NSObject
protocol, obținem această funcționalitate gratuit.
Puteți să omiteți verificarea proprietății delegaților zero
, de cand respondsToSelector:
va reveni zero
dacă proprietatea delegatului este zero
. De obicei, adaug acest control, deoarece arată clar ce testează.
A treia și ultima piesă a puzzle-ului este implementarea protocolului delegat de către obiectul delegat. Următorul fragment de cod arată crearea unui AddItemViewController
exemplu și implementarea uneia dintre metodele delegate.
- (IBAction) addItem: (id) expeditor // Initialize View Controller AddItemViewController * viewController = [[AddItemViewController alloc] init]; // Configurează controlerul de vizualizare [viewController setDelegate: self]; // Prezentator de vizualizare prezent [self presentViewController: viewController animat: DA finalizare: nil];
- (void) viewControllerDidCancel: (AddItemViewController *) viewController / / Dismați Add Item View Controller ...
Nu uitați să vă conformați clasei care acționează în calitate de delegat la AddItemViewControllerDelegate
așa cum se arată mai jos. Puteți adăuga acest lucru în interfața de clasă sau într-o extensie de clasă privată.
#import "AddItemViewController.h" @interface ViewController ()@Sfârșit
În Swift, modelul delegării este la fel de ușor de implementat și veți descoperi că Swift face delegarea puțin mai elegantă. Să implementăm exemplul de mai sus în Swift. Aceasta este ceea ce AddItemViewController
clasa arata ca in Swift.
import protocol UIKit AddItemViewControllerDelegate: NSObjectProtocol FUNC viewControllerDidCancel (viewController: AddItemViewController) FUNC viewController (viewController: AddItemViewController, didAddItem: String) FUNC viewController (viewController: AddItemViewController, validateItem: String) -> Bool class AddItemViewController: UIViewController var delegatului: AddItemViewControllerDelegate? func cancel (expeditor: AnyObject) delegate? .viewControllerDidCancel (self)
Declarația protocolului arată puțin diferit în Swift. Rețineți că AddItemViewControllerDelegate
protocol extinde NSObjectProtocol
in loc de NSObject
protocol. În Swift, clasele și protocoalele nu pot avea același nume, motiv pentru care NSObject
protocolul este denumit diferit în Swift.
delega
proprietatea este o variabilă de tip AddItemViewControllerDelegate?
. Observați semnul de întrebare la sfârșitul numelui protocolului. Proprietatea delegatului este opțională.
În Anulare(_:)
metoda, invocăm viewControllerDidCancel (_ :)
metoda delegată. Această singură linie arată cât de elegantă poate fi Swift. Desfacem în siguranță delega
înainte de invocarea metodei delegate. Nu este nevoie să verificați dacă delegatul implementează viewControllerDidCancel (_ :)
deoarece toate metodele unui protocol sunt necesare în Swift.
Să vedem acum ViewController
clasa, care implementează AddItemViewControllerDelegate
protocol. Interfața ne arată că ViewController
clasa extinde UIViewController
clasă și adoptă AddItemViewControllerDelegate
protocol.
import funcția ViewController: UIViewController, AddItemViewControllerDelegate func addItem (trimite: AnyObject) // Initialize View Controller lăsați viewController = AddItemViewController () // Configurează controlerul ViewController.delegate = auto // Prezent View Viewer presentViewController (viewController, animated: true , completare: nil) func viewControllerDidCancel (viewController: AddItemViewController) // Dismuează adăugați elementul Vizualizați controlerul ... func viewController (viewController: AddItemViewController, didAddItem: String) functionController (viewController: AddItemViewController, validateItem: String)
În adaugare element(_:)
, inițializăm o instanță a AddItemViewController
clasa, setați-o delega
proprietate, și să o prezinte utilizatorului. Rețineți că am implementat metoda fiecărui delegat din AddItemViewControllerDelegate
protocol. Dacă nu, compilatorul ne va spune că ViewController
clasa nu este conformă cu AddItemViewControllerDelegate
protocol. Încercați acest lucru comentând una dintre metodele delegate.
Delegarea este un model pe care îl veți întâlni frecvent atunci când dezvoltați aplicații pentru iOS și OS X. Cacao se bazează foarte mult pe acest model de design, astfel încât este important să vă familiarizați cu acesta.
De la introducerea blocurilor, cu câțiva ani în urmă, Apple a oferit încet un API bazat pe blocuri alternative la unele implementări ale delegațiilor. Unii dezvoltatori au urmat plumbul Apple oferind propriile alternative bazate pe blocuri. Biblioteca populară AFNetworking, de exemplu, se bazează foarte mult pe blocuri în loc de delegare, rezultând un API elegant și intuitiv.