Przejdź do głównej zawartości

iOS – singleton

Jakiś czas temu spotkałem się z sytuacją gdy jeden z moich kolegów zaimplementował klasę która w zamiarze maiła być singletonem. Sprawa wydawała by się prosta prywatny

//-----------------------------------------------------
singleton.h
@interface Singleton
   +(Singleton*) instance
@end
//-----------------------------------------------------

//-----------------------------------------------------
singleton.m
@implementation Singleton

static Singleton* instance;

+(Singleton*) instance
{

   if(!instance)
   {
     instance = [[Singleton alloc] init];
   }
   return instance;
}

+(id) init
{
  self = [super init];
  return self;
}

@end
//-----------------------------------------------------


Problem w tym że przy takiej implementacji mam do czynienia z umownym singletonem gdyż nic nie stoi na przeszkodzie aby w kodzie klienckim napisać

id fake_singleton = [[Singleton alloc] init];

Przez co istnieje możliwość utworzenia wielu instancji klasy Singleton.
Ponieważ do self przypisywać mogą jedynie metody z rodziny init można pokusić się o napisanie własnej metody np. initSingleton będąca jedynie metodą prywatną (jej prototyp nie znajduje się w pliku singleton.h). Implementacja będzie identyczna z prezentowaną wcześniej metodą init. I tu nasuwa się pierwsze pytanie, przecież prototyp metody init również nie znajduje się w pliku singleton.h zatem kod kliencki nie powinien móc się do niej odwoływać. Rzeczywiście tak jest w przypadku innych metod, niestety wszystkie klasy dziedziczą w sposób niejawny po klasie NSObject która to posiada publiczną wersje metody init wywoływanej w linijce self = [super init]. Oznacza to że pomimo iż nie udostępniamy prototypu metody init w pliku nagłówkowym jawnie, robi to za nas w sposób niejawny NSObject co prowadzi do opisanego zachowania. Implementując jedynie prywatną wersję initSintgleton klient będzie mógł wywołać metodę init dla klasy NSObject co w prezentowanym przypadku doprowadzi do utworzenie więcej niż jednej instancji naszej klasy. Jakie jest zatem poprawne rozwiązanie problemu. Oto ono:

//-----------------------------------------------------
singleton.h
@interface Singleton
+(Singleton*) instance
@end
//-----------------------------------------------------

//-----------------------------------------------------
singleton.m
@implementation Singleton

static Singleton* instance;

+(Singleton*) instance
{
  if(!instance)
  {
    instance = [[Singleton alloc] initSingleton];
  }
  return instance;
}

+(id) initSingleton
{
  self = [super init];
  return self;
}

+(id) init
{
  NSLog(@”This class is singleton, to get instance of it please     use method instance”);
  return nil;
}
@end
//-----------------------------------------------------


Jak widać do inicjalizacji obiektu wykorzystujemy metodę initSingleton, natomiast orginalną wersję metody init dostarczaną przez NSObject nadpisujemy własną wersją która zwraca zawsze nil. Oznacza to że za jej pomocą nie d się uzyskać instancji klasy Singleton. Ponadt grzecznościowo wypisujemy na konsoli logu informację o tym iż próba uzyskania obiektu tej klasy nie jest możliwe w ten sposób. Warto dodać iż NSLog jest dostępny jedynie dla kompilatu w trybie DEBUG, nie zostanie on skompilowany dla finalnej wersji kodu.

Komentarze

Popularne posty z tego bloga

Makra i preprocesor

Jako programista klasycznego C przyszło mi wielokrotnie ścierać się z makrami. Makro to zestaw instrukcji umieszczanych w kodzie są jednak interpretowane nie przez kompilator ale przez preprocesor. Preprocesor jest „pomocnikiem” kompilatora, zajmuje się on np. wstawianiem treści plików nagłówkowych do plików z kodem za pomocą instrukcji #include. Preprocesor należy rozumieć jako prymitywny edytor tekstu dokonujący „korekcji” plików z kodem źródłowym przed rozpoczęciem ich przetwarzania przez kompilator. Jakie są zalety wykorzystywania tych rozwiązań w kodzie? Tak naprawdę w języku C w czasach przed wprowadzeniem słowa kluczowego inline umożliwiały wstawianie kodu we wskazane miejsce. Należy bowiem pamiętać że każdorazowe wstawienie makroinstrukcji powoduje ingerencję w kod źródłowy (innymi słowy we wskazanym miejscu zostanie wstawiony stosowny fragment kodu). Łatwo obserwowalnym efektem częstego wykorzystywania makr w plikach z kodem jest rozrost pliku binarnego oraz jego szybsze d

C++11 Iterowanie

Zasadniczo iterowanie po elementach tablicy/vectora/listy itp. nie jest niczym nowym, interesującym ani pasjonującym, ot szara codzienność. Przyjrzyjmy się zatem jak robimy to najczęściej: class Image { public:   Image();   void rotate(float angle);   void display();   void* serialize(); }; class ShowImage { public:   void operator()(Image image)   {     image.display();   } }; //... std::vector<Image> imageCollection(10); for(int i=0; i<imageCollection.size(); ++i) {   imageCollection[i].display(); } for( std::vector<Image>::iterator                          currentImage = imageCollection.begin();                         currentImage < imageCollection.end();                         ++currentImage ) {   currentImage->display(); } std::for_each(imageCollection.begin(), imageCollection.end(),               ShowImage()); Dla naszych potrzeb stworzyliśmy sp

C++11 Variadic templates – szablony ze zmienną liczba parametrów

Witam w nowym roku. Dziś będzie o nowym elemencie szablonów czyli o szablonach ze zmienną liczbą parametrów. Ich implementacja i zachowanie różni się nieco od klasycznych szablonów. Nowa funkcjonalność pozwala na tworzenie bezpiecznych list typów. template<typename ...Ts> void variadic_template(){} Pierwszą nowością jest zastosowanie ...(wielokropka) przy określaniu typów szablonu. W ten sposób sygnalizujemy mnogość typów. Niestety nie mamy możliwości iterowania po kolejnych typach wewnątrz naszego szablonu. Ponadto taka definicja pozwala na stworzenie/wywołanie naszej szablonowej funkcji bez typów. variadic_template<>(); Aby tego uniknąć można uciec się do następującej sztuczki: template<typename T1, typename ...Ts> void variadic_template(T1 arg, Ts... args) W powyższym przykładzie jawnie wymuszamy podanie przynajmniej jednego typ dla naszej funkcji szablonowej. Jak już wspomniałem nie możemy jawnie iterować po typach przekazanych do