Pytasz, czy to „najlepszy sposób na stworzenie singletonu”.
Po pierwsze, tak, jest to rozwiązanie bezpieczne dla wątków. Ten dispatch_oncewzó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 instancetypei [[self alloc] init]jest potencjalnie mylące, gdy jest używane w połączeniu z singletonami.
Zaletą tego instancetypejest 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 staticw tej metodzie przedstawia się wyzwania związane z podklasowaniem. Co jeśli ImageCachei BlobCachesingletons były zarówno podklasy ze Cachenadrzędnej bez wdrożenie własnej sharedCachemetody?
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_onceuż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 NSURLSessionsingleton 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, unavailablekwalifikator jest włączony initi newjest ostrożny.