2012-12-28 18 views
5

Moje aplikacje używają [NSUserDefaults standardUserDefaults] jako szybkiej i brudnej bazy danych do przechowywania stanu o użytkowniku i samej aplikacji. Problem z NSUserDefaults polega na tym, że jego elastyczność pozwala na duży bałagan w dół, na przykład, gdy różne pliki ustawiają się i czytają różne klucze w słowniku na swój własny sposób. Nie możesz wymuszać reguł, możesz zepsuć kluczową nazwę itp.Który wzór do pakowania NSUserDefaults?

Napisałem prostą, singletonową "menedżersko-stylową" okładkę dla NSUserDefaults, która dba o ustawienie domyślnych wartości, gdy jest używana, ukrywa nazwa kluczy używanych do pobierania wartości i enkapsuluje pewną dodatkową logikę, na przykład kodowanie do NSData, podczas przechowywania i pobierania obiektów ze sklepu.

W tym momencie są to właściwości wspierane przez read/set accessor, ale coś mnie o to niepokoi i zastanawiam się, czy istnieje bardziej elegancki sposób na osiągnięcie tego samego rezultatu. Jest całkiem sporo podstaw, a składnia staje się nieco nieprzyjemna. Aby dać przykład:

.h:

@interface UserDefaultsManager: NSObject 

+ (UserDefaultsManager *)sharedInstance; 

@property (nonatomic, assign) NSInteger somethingImTracking; 

@end 

i .m:

NSString * const kSomethingImTracking= @"SomethingImTracking"; 

@implementation UserDefaultsManager 

[...] 

- (NSInteger)somethingImTracking 
{ 
    return [[[NSUserDefaults standardUserDefaults] objectForKey:kSomethingImTracking] intValue]; 
} 

- (void)setSomethingImTracking:(NSInteger)somethingImTracking 
{ 
    [[NSUserDefaults standardUserDefaults] setInteger:somethingImTracking forKey:kSomethingImTracking]; 
} 

i uzyskać dostęp do:

NSInteger foo = [UserDefaultsManager sharedInstance].somethingImTracking; 
+1

Robię * dokładnie * to samo w jednej z moich aplikacji, aby hermetyzować zestaw ustawień preferencji obsługiwanych przez moją aplikację. BTW - powinieneś zrobić static kSomethingImTracking, ponieważ jest on używany tylko w tym pliku .m. – rmaddy

+0

@rmaddy Świetna wskazówka, dzięki! –

+0

Możesz również chcieć, aby twoje 'setSomethingImTracking' było takie, że (a) rozsądne byłoby robienie' synchronizacji' po ustawieniu wartości; i (b) może zrobić 'willChangeValueForKey' przed ustawieniem wartości i' didChangeValueForKey' po ustawieniu, na wypadek gdyby kiedykolwiek zaimplementowałeś KVO w przyszłości. Zgadzam się jednak, że jest to dobry sposób na wyodrębnienie szczegółów ustawień 'NSUserDefaults'. – Rob

Odpowiedz

4

Osobiście używam stałe ciągów znaków do zapamiętywania kluczowe nazwy i po prostu uzyskaj bezpośredni dostęp do obiektu domyślnego użytkownika, ale nie używam wartości domyślnych w dużym stopniu lub w wielu różnych językach osły.

Jednym z ulepszeń, które wprowadziłbym do twojego kodu, jest posiadanie ich jako metod klasowych. Nie ma żadnej korzyści dla singletona (nie utrzymujesz żadnego stanu, to wszystko jest w obiekcie domyślnym), a to wymaga fragmentu brzydkiego powtarzalnego kodu (...sharedInstance) z wzorca użycia.

synchronize nie jest konieczne uwzględnianie przy każdym ustawieniu. Jest potrzebny tylko w przypadku szybkiego dostępu do wartości domyślnych z różnych wątków. System operacyjny również sam je okresowo.

+0

To dobra uwaga, jestem z pewnością niepotrzebnie "izolować przyszłe" klasy dla potencjalnego przyszłego stanu wewnętrznego. Jedyną korzyścią, którą oferuje singleton, jest leniwe uruchamianie "registerDefaults" za każdym razem, gdy jest używany po raz pierwszy, co łatwo można zastąpić oddzielnym wywołaniem w kodzie delegata aplikacji. –

+0

Kolejną zaletą posiadania singletonu jest dostarczanie instancji NSUserDefaults, z której będziesz korzystać. Możesz mieć nieruchomość statyczną, ale to wygląda na bałagan. I naprawdę możesz zadbać tylko o zastrzyk uzależnienia, jeśli zamierzasz pisać testy lub jesteś pedantem OOP. – rob5408

+0

@ rob5408 w iOS, istnieje tylko jedna instancja domyślna. – jrturton