scris de Reinder de Vries pe 10 ianuarie 2019 în dezvoltarea aplicațiilor, Swift
un singleton este o clasă din care există exact o instanță, care poate fi accesată la nivel global. Cum creezi un singleton în Swift? Și de ce ar trebui sau nu ar trebui?
în acest tutorial, vom arunca cu capul în singletons în Swift. Aflați ce este modelul de design singleton și de ce este util. Vom discuta sintaxa pentru crearea singletons în Swift. Și vom intra în cazuri bune și rele de utilizare pentru singletons.
gata? Să mergem.
- Ce Este Un Singleton?
- Codificarea Unui Singleton În Swift
- Când Se Utilizează Singleton
- Lecturi Suplimentare
Ce Este Un Singleton?
un singleton este o clasă din care există o singură instanță. Câteva exemple:
- o companie are un singur
CEO
- o clasă API are doar o singură coadă de solicitare serială
- un sistem de operare are un singur sistem de fișiere
- un corp al sistemului solar se învârte în jurul unui punct gravitațional
- o aplicație care face I/O are un singur implicit
FileManager
- un avion are o singură punte de zbor
al doilea atribut al unui Singleton este că are un punct global de acces. Puteți accesa un singleton, de ex. funcții de apel pe ea, de oriunde în codul aplicației.
deci, pentru a rezuma:
- un singleton este o clasă care are o singură instanță
- poate fi accesat la nivel global, adică oriunde în codul dvs.
în dezvoltarea practică a iOS, utilizați singletons des. Clase tipice cum ar fi NotificationCenter
, UserDefaults
, SKPaymentQueue
și FileManager
au shared
sau default
proprietăți care sunt singletons.
în alte momente, ați putea dori să creați singur un singleton. Un caz bun de utilizare este o clasă API
care expune instanța singleton prin proprietatea sa shared
. Utilizați API.shared.makeAPICall()
pentru a accesa API – ul printr-o singură instanță unificată. Acest lucru vă permite, de exemplu, să gestionați apelurile API în serie.
înainte de a discuta când singletonii sunt cel mai bine folosiți (și când nu), să aflăm cum să codificăm un singleton în Swift.
angajați-vă ca dezvoltator iOS
Aflați cum să construiți Aplicații iOS 14 cu Swift 5
Înscrieți-vă la cursul meu de dezvoltare iOS și aflați cum să vă începeți cariera ca dezvoltator iOS profesionist.
codificarea unui Singleton în Swift
acesta este cel mai bun mod de a crea un singleton în Swift:
class API{ static let shared = API() private init() { // Set up API instance }}
și iată cum utilizați singleton:
API.shared.doSomething()
creăm o clasă API
care are o proprietate statică numită shared
. Această proprietate nu poate fi modificată odată setată, deoarece este o constantă și este declarată static.
asta înseamnă că putem accesa proprietatea shared
prin clasa API
. Aceasta este adesea numită proprietate de clasă. Comparați acest lucru cu o proprietate de instanță normală, care poate fi accesată numai printr-o instanță a unei clase.
ceea ce este interesant este că proprietatea shared
inițializează o instanță de API
în clasa API
. Creăm un obiect API
care poate fi accesat prin clasa API
. Dar există mai multe…
inițializatorul de clasă init()
este marcat cu private
. Acest cuvânt cheie private
asigură că clasa API
poate fi inițializată numai în clasa API
.
cu alte cuvinte, nu puteți crea o instanță de API
în afara clasei API
! Acest lucru asigură că obiectul API
pe care l-am creat este singura instanță din Codul nostru. La urma urmei, nu poți crea mai mult din ea.
și acum ne-am asigurat API
clasa este conformă cu cele două atribute ale unui singleton:
- datorită proprietății statice
shared
, instanțaAPI
poate fi accesată la nivel global - datorită
private init()
, clasaAPI
nu poate fi inițializată în afara claseiAPI
toate acestea vă pot suna puțin abstract, așa că haideți să extindem exemplul anterior cu un cod mai practic. Iată ce:
clasa API
este în mare parte aceeași. Este încă un singleton, și încă folosește acele static let shared = API()
și private init()
biți de cod.
Iată ce s-a schimbat:
- clasa
API
are acum o proprietateisRequestPending
. Acest lucru este în cazul în care pericolul începe… vezi cumisRequestPending
Boolean asigură că doar o singură cerere API se poate face la un moment dat? (Rețineți căisRequestPending
este o proprietate instanță.) - clasa
API
are, de asemenea, o funcțiemakeAPIRequest()
. Imaginați-vă că putem folosi această funcție pentru a obține unele date înapoi de la un API webservice, cum ar fi Twitter. în funcție, puteți vedea că o solicitare poate fi făcută numai atunci când nicio altă solicitare nu este în curs. - clasa
API
are, de asemenea, o funcțieonReturnAPIRequest()
. Această funcție este invocată atunci când cererea API revine, adică datele online au fost descărcate în aplicație.isRequestPending
boolean este setat lafalse
din nou, iar datele de solicitare sunt procesate.
și iată cum putem folosi API
singleton oriunde în codul nostru:
API.shared.makeAPIRequest()
mai e ceva ce trebuie să discutăm. Clasa API
gestionează acum ceva numit stat. Puteți vedea „starea” ca pe un sentiment: ori ești fericit, ori ești trist, ori ești supărat și așa mai departe. Puteți trece de la o stare la alta.
clasa API
poate comuta între două stări:
- o stare în care
isRequestPending
estefalse
- o stare în care
isRequestPending
estetrue
după cum veți afla în secțiunea următoare, de stat și singletons poate face tot felul de ravagii pe codul. Gestionarea de stat prost este cel mai mare motiv unic pentru utilizarea abuzivă singleton.
când să utilizați Singletons
când utilizați singletons? Cartea Design Patterns: Elements of refolosibile Object-Oriented Software by the Gang of Four are următoarele de spus. Utilizați modelul singleton atunci când:
- trebuie să existe exact o instanță a unei clase și trebuie să fie accesibilă clienților dintr-un punct de acces bine cunoscut
- când singura instanță ar trebui să fie extensibilă prin subclasare, iar Clienții ar trebui să poată utiliza o instanță extinsă fără a-și modifica codul
este complex, dar ceea ce se reduce:
- utilizați un singleton atunci când codul dvs. nu necesită mai mult de o instanță a unei clase (adică CEO-ul companiei)
- și când trebuie să fie accesibil de oriunde din codul dvs. (adică., sistemul de fișiere)
un alt caz de utilizare este subclasarea. O variabilă globală din codul dvs. nu poate fi ușor subclasată, deci de aceea utilizați o clasă singleton. În plus, singletons pot fi testate unitar prin utilizarea injecției de dependență. Înlocuiți instanța API
cu o instanță APIMock
și obțineți capacitatea de a testa apelurile API fără a face cererile reale de rețea.
și când nu folosiți singletons? Pentru a răspunde la această întrebare, va trebui să ne întoarcem la principiul de stat pe care l-am discutat mai devreme.
o capcană comună pentru dezvoltatorii iOS începători este de a gestiona prost starea și dependențele sale. Imaginați-vă că construiți o aplicație care utilizează clasa API
cu care am lucrat mai devreme.
de fiecare dată când extindeți clasa API, abordați din ce în ce mai multe proprietăți, cum ar fi:
- o proprietate
userID
care ține evidența utilizatorului conectat, odată ce apelul APIlogin()
a fost efectuat - o proprietate
tweets
cu date Twitter, odată ce apelulgetTweets()
a fost efectuat - o proprietate
spinner
cu unUIActivityIndicatorView
pe care îl adăugați la un controler de vizualizare atunci când a început o cerere
la început, acest lucru are mult sens de făcut. La urma urmei, clasa API
poate fi accesată oriunde în codul dvs. Deci, în controlerul de vizualizare Tweet puteți utiliza matricea API.shared.tweets
, iar în controlerul de setări puteți utiliza userID
pentru a spune rapid API-ului ale cărui setări să se schimbe.
din păcate, starea ta este acum peste tot. Clasa API
are dependențe la o grămadă de clase care nu sunt legate de responsabilitatea unică a clasei API. Codul tău a devenit un castron de spaghete, toate încurcate. Codul poate funcționa OK, dar este imposibil să se mențină și să se extindă.
să ne uităm la un exemplu. Funcția onReturnAPIRequest()
pe care am definit-o mai devreme este pe punctul de a deveni strâns cuplată…
Iată ce avem în vedere:
-
onReturnAPIRequest()
se numește atunci când se întoarce o cerere API webservice, adică atunci când datele intră în aplicație. Aceste date trebuie să meargă undeva-un controler de vizualizare Tweet, de exemplu. Cum transmiteți datele de laAPI
către controlerul de vizualizare? - o alegere evidentă este de a crea doar o referință la
viewController
înAPI
clasă. Când datele intră, puteți codifica ceva de genulviewController.tweets = tweetsData
. Din păcate, aceasta este o arhitectură slabă, deoarece acumAPI
și controlerul de vizualizare sunt strâns cuplate. Este imposibil (sau greu) să testați unitatea și este posibil să creați probleme atunci când extindeți oricare dintre clase. - este mai bine să alegeți un mecanism care să nu cupleze strâns ambele clase. O opțiune ar fi trecerea unei închideri la
onReturnAPIRequest()
, care este executată la returnarea cererii. Această închidere poate conține apoi cod pentru a gestiona datele primite. O altă opțiune ar fi utilizareaNotificationCenter
pentru a transmite datele către controlerul de vizualizare sau pentru a utiliza o clasăDatabase
pentru a gestiona datele.
modelul de design singleton a câștigat unele controverse, pur și simplu pentru că este ușor de abuzat. Când utilizați singletons, fiți atenți la starea și dependențele. Doar pentru că este ușor să ai acces global la stat, nu înseamnă că este o idee bună.
angajați-vă ca dezvoltator iOS
Aflați cum să construiți Aplicații iOS 14 cu Swift 5
Înscrieți-vă la cursul meu de dezvoltare iOS și aflați cum să vă începeți cariera ca dezvoltator iOS profesionist.
lecturi suplimentare
și asta e tot ce este de făcut! Cunoașterea singletons este un obiectiv util, mai ales dacă sunteți interesat de arhitectura aplicațiilor și proiectarea sistemelor.