Próbuję wypracować odpowiedni model singletonu do użytku w Swift. Do tej pory udało mi się uzyskać bezpieczny dla wątków model działający jako:
class var sharedInstance: TPScopeManager {
get {
struct Static {
static var instance: TPScopeManager? = nil
}
if !Static.instance {
Static.instance = TPScopeManager()
}
return Static.instance!
}
}
Zawijanie instancji singletonowej w strukturze statycznej powinno pozwolić jednej instancji, która nie koliduje z instancjami singletonowymi bez skomplikowanych schematów nazewnictwa, i powinna uczynić to dość prywatnym. Oczywiście ten model nie jest bezpieczny dla wątków. Próbowałem więc dodać dispatch_once
do całości:
class var sharedInstance: TPScopeManager {
get {
struct Static {
static var instance: TPScopeManager? = nil
static var token: dispatch_once_t = 0
}
dispatch_once(Static.token) { Static.instance = TPScopeManager() }
return Static.instance!
}
}
Ale pojawia się błąd kompilatora w dispatch_once
wierszu:
Nie można przekonwertować typu wyrażenia „Void” na typ „()”
Wypróbowałem kilka różnych wariantów składni, ale wszystkie wydają się mieć takie same wyniki:
dispatch_once(Static.token, { Static.instance = TPScopeManager() })
Jakie jest właściwe użycie dispatch_once
Swift? Początkowo myślałem, że problem dotyczy bloku z powodu ()
komunikatu o błędzie, ale im dłużej na niego patrzę, tym bardziej myślę, że może to być kwestia dispatch_once_t
prawidłowego zdefiniowania.
@lazy
powinna być bezpieczna dla wątków.Static.instance = TPScopeManager()
wymusza typ instancji. Jeśli użyjesz czegoś takiegoStatic.instance = self()
z wymaganym inicjatorem, zostanie wygenerowana odpowiednia klasa typu. Mimo to jest to ważna rzecz do odnotowania, tylko raz dla wszystkich instancji w hierarchii! Pierwszym typem do zainicjowania jest zestaw typów dla wszystkich instancji. Nie sądzę, żeby cel-c zachowywał się tak samo.Odpowiedzi:
tl; dr: Użyj metody stałej klasowej, jeśli używasz Swift 1.2 lub nowszej, i zagnieżdżonej struktury strukturalnej, jeśli potrzebujesz obsługi wcześniejszych wersji.
Z mojego doświadczenia ze Swiftem są trzy podejścia do implementacji wzorca Singleton, które wspierają leniwe inicjowanie i bezpieczeństwo wątków.
Stała klasy
To podejście obsługuje leniwe inicjowanie, ponieważ Swift leniwie inicjuje stałe klas (i zmienne) i jest wątkowo bezpieczny z definicji
let
. Jest to teraz oficjalnie zalecany sposób utworzenia singletonu.Stałe klas zostały wprowadzone w Swift 1.2. Jeśli potrzebujesz obsługiwać wcześniejszą wersję Swift, użyj poniższego podejścia do struktury zagnieżdżonej lub stałej globalnej.
Struktura zagnieżdżona
Używamy tutaj stałej statycznej zagnieżdżonej struktury jako stałej klasy. Jest to obejście problemu braku stałych statycznych klas w Swift 1.1 i wcześniejszych wersjach, i nadal działa jako obejście braku stałych statycznych i zmiennych w funkcjach.
dispatch_once
Tradycyjne podejście do celu C przeniesiono do Swift. Jestem całkiem pewien, że nie ma przewagi nad zagnieżdżoną strukturą strukturalną, ale i tak ją tutaj umieszczam, ponieważ uważam różnice w składni za interesujące.
Zobacz ten projekt GitHub dla testów jednostkowych.
źródło
init
należy również zadeklarować,private
aby zagwarantować, że przez całe życie aplikacji będzie istniała tylko jedna instancja obiektu?final
nie była podklasą; oraz (b) oświadczenie, żeinit
metoda jestprivate
taka, że nie można przypadkowo utworzyć gdzieś innej instancji.Ponieważ Apple wyjaśniło, że statyczne zmienne struktury są inicjowane zarówno leniwie, jak i zawinięte
dispatch_once
(patrz uwaga na końcu postu), myślę, że moim ostatecznym rozwiązaniem będzie:Wykorzystuje to automatyczną, leniwą, bezpieczną dla wątków inicjalizację statycznych elementów konstrukcyjnych, bezpiecznie ukrywa rzeczywistą implementację przed konsumentem, utrzymuje wszystko w kompaktowych przedziałach dla czytelności i eliminuje widoczną zmienną globalną.
Apple wyjaśniło, że leniwy inicjator jest bezpieczny dla wątków, więc nie ma potrzeby
dispatch_once
ani podobnych zabezpieczeńod tutaj
źródło
private init() {}
w celu dalszego egzekwowania faktu, że ta klasa nie jest przeznaczona do tworzenia instancji z zewnątrz.W przypadku Swift 1.2 i nowszych wersji:
Z dowodem poprawności (wszystkie zasługi tutaj ), nie ma teraz żadnego powodu, aby używać dowolnej z poprzednich metod singletonów.
Aktualizacja : Jest to teraz oficjalny sposób definiowania singletonów, jak opisano w oficjalnych dokumentach !
Jeśli chodzi o obawy dotyczące korzystania
static
vsclass
.static
powinien być tym, którego należy używać, nawet gdyclass
zmienne stają się dostępne. Singletonów nie należy klasyfikować do podklas, ponieważ spowodowałoby to wiele wystąpień podstawowego singletonu. Używaniestatic
wymusza to w piękny, szybki sposób.W przypadku Swift 1.0 i 1.1:
Dzięki ostatnim zmianom w Swift, głównie nowym metodom kontroli dostępu, teraz skłaniam się ku czystszemu sposobowi używania zmiennej globalnej dla singletonów.
Jak wspomniano w artykule na blogu Swift tutaj :
Ten sposób tworzenia singletonów jest bezpieczny, szybki, leniwy, a także połączony z ObjC za darmo.
źródło
private init() {}
jako inicjatorSingletonClass
. aby zapobiec wystąpieniu z zewnątrz.Swift 1.2 lub nowszy obsługuje teraz zmienne statyczne / stałe w klasach. Możesz więc po prostu użyć stałej statycznej:
źródło
Jest lepszy sposób, aby to zrobić. Możesz zadeklarować zmienną globalną w swojej klasie powyżej deklaracji klasy w następujący sposób:
To po prostu wywołuje domyślny init lub cokolwiek init, a zmienne globalne są
dispatch_once
domyślnie w Swift. Następnie w dowolnej klasie, w której chcesz uzyskać referencję, po prostu wykonaj następujące czynności:Zasadniczo możesz więc pozbyć się całego bloku współdzielonego kodu instancji.
źródło
TPScopeManager.sharedInstance.doIt()
cały czas pisać , po prostu nazwij swoją klasęTPScopeManagerClass
, umieść tę deklarację obok klasypublic let TPScopeManager = TPScopeManagerClass()
, a podczas używania po prostu napiszTPScopeManager.doIt()
. Bardzo czysto!TPScopeManager
stoi na przeszkodzie, aby stworzyć dodatkowe wystąpienia , dlatego też nie jest to singleton z definicji.Singletons Swift są wystawione w ramach kakao jak funkcje klasy, na przykład
NSFileManager.defaultManager()
,NSNotificationCenter.defaultCenter()
. Dlatego bardziej sensowne jest, aby funkcja klasy odzwierciedlała to zachowanie, niż zmienna klasowa, jak niektóre inne rozwiązania. na przykład:Odzyskaj singletona przez
MyClass.sharedInstance()
.źródło
static
właściwościami.Zgodnie z dokumentacją Apple wiele razy powtarzano, że najłatwiejszym sposobem na to w Swift jest właściwość typu statycznego:
Jeśli jednak szukasz sposobu na wykonanie dodatkowej konfiguracji poza prostym wywołaniem konstruktora, sekret polega na użyciu natychmiast wywołanego zamknięcia:
Gwarantuje to, że jest bezpieczny dla wątków i leniwie inicjowany tylko raz.
źródło
Swift 4+
źródło
Patrząc na przykładowy kod Apple, natknąłem się na ten wzorzec. Nie jestem pewien, jak Swift radzi sobie ze statyką, ale byłoby to bezpieczne dla wątków w C #. Podaję zarówno właściwość, jak i metodę dla współdziałania Objective-C.
źródło
dispatch_once
. Stawiam na twój styl. :)class
w deklaracji klasowej nie ma odpowiednikastatic
deklaracji strukturalnej?dispatch_once
możliwości.W skrócie,
Możesz przeczytać Pliki i inicjowanie
źródło
Jeśli planujesz używać swojej klasy singleton Swift w Objective-C, to ustawienie spowoduje, że kompilator wygeneruje odpowiednie nagłówki podobne do Objective-C:
Następnie w klasie Objective-C możesz nazwać singletona tak, jak to robiłeś w dniach sprzed Swift:
To tylko moja prosta implementacja.
źródło
NSFileManager.defaultManager()
, ale nadal używa leniwych, bezpiecznych dla wątków mechanizmów statycznych elementów Swift.Pierwsze rozwiązanie
Później w kodzie:
Drugie rozwiązanie
A później w kodzie będziesz mógł przechowywać nawiasy klamrowe dla mniejszego zamieszania:
źródło
Nazwij to;
źródło
init
jakoprivate
, ale także do tworzeniasharedMyModel
jakofinal
! Ze względu na przyszłych czytelników w Swift 3 możemy chcieć zmienić nazwęsharedMyModel
na zwykłąshared
.Posługiwać się:
Jak używać:
źródło
Najlepszym podejściem w Swift powyżej 1.2 jest singleton jednowierszowy, ponieważ -
Aby dowiedzieć się więcej o tym podejściu, możesz odwiedzić ten link .
źródło
NSObject
podklasa ?. Poza tym wydaje się, że jest to zasadniczo to samo, co stackoverflow.com/a/28436202/1187415 .Z Apple Docs (Swift 3.0.1),
źródło
Sugerowałbym
enum
, jak byście używali w Javie, npźródło
Dla przykładu, oto przykładowa implementacja Nested Struct Jacka Wu / hpique Singleton. Implementacja pokazuje również, jak może działać archiwizacja, a także niektóre funkcje towarzyszące. Nie mogłem znaleźć tego pełnego przykładu, więc mam nadzieję, że to komuś pomoże!
A jeśli nie rozpoznałeś niektórych z tych funkcji, oto mały, żywy plik narzędzia Swift, którego używałem:
źródło
W trybie szybkiego możesz utworzyć klasę singleton w następujący sposób:
źródło
Wolę tę implementację:
źródło
Mój sposób wdrożenia w Swift ...
ConfigurationManager.swift
Uzyskaj dostęp do globalDic z dowolnego ekranu aplikacji poniżej.
Czytać:
Pisać:
źródło
Jedyne właściwe podejście znajduje się poniżej.
Mieć dostęp
Powody:
static
gwarantuje się, że właściwość type zostanie leniwie zainicjowana tylko raz, nawet jeśli jest dostępna dla wielu wątków jednocześnie, więc nie trzeba jej używaćdispatch_once
init
metody, więc instancja nie może zostać utworzona przez inne klasy.final
klasa, ponieważ nie chcesz, aby inne klasy dziedziczyły klasę Singleton.źródło
static let sharedInstance = Singleton()
Po zobaczeniu implementacji Davida wydaje się, że nie ma potrzeby używania funkcji klasy singleton,
instanceMethod
ponieważlet
robi ona prawie to samo, cosharedInstance
metoda klasowa. Wszystko, co musisz zrobić, to zadeklarować jako globalną stałą i to by było na tyle.źródło
źródło
dispatch_once
ponieważ inicjalizacja zmiennych statycznych jest leniwa i automatycznie chroniona przezdispatch_once
Apple, z tego powodu zaleca używanie statyki zamiast dispatch_once.Szybka realizacja singletonu w przeszłości to nic innego jak trzy sposoby: zmienne globalne, zmienne wewnętrzne i sposoby dispatch_once.
Oto dwa dobre singletony. (Uwaga: bez względu na to, jaki rodzaj pisania będzie musiał zwrócić uwagę na metodę inicjalizacji metody init (), ponieważ w Swift wszystkie ustawienia domyślne konstruktora obiektu są publiczne, należy zmienić przepis na init, który można przekształcić w prywatny , zapobiegaj domyślnym metodom inicjowania innych obiektów tej klasy „()”, aby utworzyć obiekt).
Metoda 1:
Metoda 2:
źródło
Jest to najprostszy z zabezpieczonymi wątkami. Żaden inny wątek nie może uzyskać dostępu do tego samego obiektu singleton, nawet jeśli tego chcą. Szybki 3/4
źródło
Wymagałem, aby mój singleton zezwalał na dziedziczenie i żadne z tych rozwiązań nie pozwalało na to. Więc wpadłem na to:
Singleton.sharedInstance()
pierwszy, zwróci instancjęSingleton
SubSingleton.sharedInstance()
pierwszy zwróci instancjęSubSingleton
utworzonej.SubSingleton.sharedInstance()
jestSingleton
to prawda i używana jest ta sama instancja.Problem z tym pierwszym nieprzyzwoitym podejściem polega na tym, że nie mogę zagwarantować, że podklasy zaimplementują
dispatch_once_t
i upewnią się, żesharedInstanceVar
zostanie zmodyfikowany tylko raz na klasę.Spróbuję to doprecyzować, ale byłoby interesujące sprawdzić, czy ktoś ma do tego silne uczucia (poza tym, że jest pełny i wymaga ręcznej aktualizacji).
źródło
To jest moja realizacja. Zapobiega także tworzeniu przez programistę nowej instancji:
źródło
private init
został już zasugerowany tutaj: stackoverflow.com/a/28436202/1187415 .Używam następującej składni:
Działa to od Swift 1.2 do 4 i ma kilka zalet:
Singleton.instance
źródło