Pytasz, czy to „najlepszy sposób na stworzenie singletonu”.
Po pierwsze, tak, jest to rozwiązanie bezpieczne dla wątków. Ten dispatch_once
wzór jest nowoczesnym, bezpiecznym dla wątków sposobem generowania singletonów w Objective-C. Nie martw się tam.
Zapytałeś jednak, czy jest to „najlepszy” sposób na zrobienie tego. Należy jednak uznać, że instancetype
i [[self alloc] init]
jest potencjalnie mylące, gdy jest używane w połączeniu z singletonami.
Zaletą tego instancetype
jest to, że jest to jednoznaczny sposób zadeklarowania, że klasę można podklasować bez uciekania się do pewnego rodzaju id
, jak to robiliśmy w przeszłości.
Ale static
w tej metodzie przedstawia się wyzwania związane z podklasowaniem. Co jeśli ImageCache
i BlobCache
singletons były zarówno podklasy ze Cache
nadrzędnej bez wdrożenie własnej sharedCache
metody?
ImageCache *imageCache = [ImageCache sharedCache]; // fine
BlobCache *blobCache = [BlobCache sharedCache]; // error; this will return the aforementioned ImageCache!!!
Aby to zadziałało, musisz upewnić się, że podklasy implementują własną sharedInstance
(lub jakkolwiek to nazwiesz dla konkretnej klasy) metodę.
Podsumowując, twój oryginalny sharedInstance
wygląd będzie obsługiwał podklasy, ale nie będzie. Jeśli zamierzasz wspierać podklasowanie, dołącz przynajmniej dokumentację, która ostrzega przyszłych programistów, że muszą zastąpić tę metodę.
Aby uzyskać najlepszą interoperacyjność z Swift, prawdopodobnie chcesz zdefiniować to jako właściwość, a nie metodę klasy, np .:
@interface Foo : NSObject
@property (class, readonly, strong) Foo *sharedFoo;
@end
Następnie możesz napisać getter dla tej właściwości (implementacja dispatch_once
użyłaby zaproponowanego wzorca):
+ (Foo *)sharedFoo { ... }
Zaletą tego jest to, że jeśli użytkownik Swift z niego skorzysta, zrobiłby coś takiego:
let foo = Foo.shared
Uwaga, nie ma ()
, ponieważ zaimplementowaliśmy go jako właściwość. Począwszy od wersji Swift 3, w ten sposób zazwyczaj uzyskuje się dostęp do singletonów. Tak więc zdefiniowanie go jako właściwości ułatwia tę interoperacyjność.
Nawiasem mówiąc, jeśli spojrzysz na to, jak Apple definiuje ich singletony, jest to wzorzec, który przyjęli, np. Ich NSURLSession
singleton jest zdefiniowany w następujący sposób:
@property (class, readonly, strong) NSURLSession *sharedSession;
Inną, bardzo niewielką kwestią dotyczącą szybkiej interoperacyjności była nazwa singletonu. Najlepiej, jeśli zamiast tego możesz podać nazwę typu sharedInstance
. Na przykład, jeśli klasa była Foo
, możesz zdefiniować właściwość singleton jako sharedFoo
. Lub jeśli klasa była DatabaseManager
, możesz zadzwonić do nieruchomości sharedManager
. Następnie użytkownicy Swift mogliby:
let foo = Foo.shared
let manager = DatabaseManager.shared
Oczywiście, jeśli naprawdę chcesz używać sharedInstance
, zawsze możesz zadeklarować nazwę Swift, jeśli chcesz:
@property (class, readonly, strong) Foo* sharedInstance NS_SWIFT_NAME(shared);
Oczywiście, pisząc kod Celu-C, nie powinniśmy pozwolić, aby interoperacyjność Swift przeważała nad innymi względami projektowymi, ale mimo to, jeśli możemy napisać kod, który z wdziękiem obsługuje oba języki, jest to preferowane.
Zgadzam się z innymi, którzy podkreślają, że jeśli chcesz, aby był to prawdziwy singleton, w którym programiści nie mogą / nie powinni (przypadkowo) tworzyć własnych instancji, unavailable
kwalifikator jest włączony init
i new
jest ostrożny.