Używanie stałej NSString jako klucza dla NSUserDefaults

86

Używam NSUSerDefaults do przechowywania preferencji użytkownika. Pamiętam, że czytałem gdzieś, że ustawienie kluczy jako stałych to dobry pomysł - i zgadzam się. Obecnie posiadam następujący kod:

[[NSUserDefaults standardUserDefaults]
        setObject:[NSNumber numberWithInt:polygon.numberOfSides] 
           forKey:@"polygonNumberOfSides"];

Próbowałem to zmienić na:

@implementation Controller

NSString const *kPolygonNumberOfSides = @"polygonNumberOfSides";

-(void)savePolygonInfo {
    [[NSUserDefaults standardUserDefaults]
            setObject:[NSNumber numberWithInt:polygon.numberOfSides] 
               forKey:kPolygonNumberOfSides];
}

Chociaż to działa, generuje „ warning: passing argument 1 of 'objectForKey:' discards qualifiers from pointer target type”. Chcę, aby mój kod był wolny od ostrzeżeń kompilatora. Jak mogę naprawić to ostrzeżenie?

Olly
źródło

Odpowiedzi:

207

Powinieneś użyć:

NSString * const kPolygonNumberOfSides = @"..."; // const pointer

zamiast:

NSString const * kPolygonNumberOfSides = @"..."; // pointer to const

Pierwsza to stały wskaźnik do obiektu NSString, a druga to wskaźnik do stałego obiektu NSString.

To subtelna różnica. Ostrzeżenie kompilatora ma miejsce, ponieważ setObject:forKey:jest zadeklarowane w następujący sposób:

- (void)setObject:(id)value forKey:(NSString *)defaultName;

Oczekuje, że defaultNameargument będzie typu NSString *. Kiedy zamiast tego przekazujesz wskaźnik do stałej, dajesz jej coś innego.

Aktualizacja: Chcę zaznaczyć, że te stałe powinny być zdefiniowane tak,staticjakby miały być używane tylko z jednego pliku. Mówię to, ponieważ sam napotkałem ten problem: jeśli nie zadeklarujesz ich jako statyczne, będą istnieć w globalnej przestrzeni nazw i nie będziesz mógł użyć zmiennej o tej samej nazwie w innym pliku. zobacz Stałe w celu C, aby uzyskać więcej informacji. Aby wyjaśnić na przykładzie, właśnie tego używam obecnie w przypadku kluczy, których potrzebuję tylko w jednym.mpliku:

static NSString * const kSomeLabel = @"...";
e.James
źródło
1
NSString * const foodziała, ponieważ NSStringjest niezmienny, a wskaźnik jest niezmienny, więc nigdy nie może się zmienić, prawda? Przypominam sobie również z C ++, że constjest to niejawne static(optymalizacja kompilatora), więc nie ma potrzeby tego wywoływać. Czy to prawda również tutaj?
Ternary
32

Nie używaj constz obiektami Objective-C, tak naprawdę nie zostały zaprojektowane do używania tego. NSStringobiekty (wśród wielu innych) są już domyślnie niezmienne ze względu na ich projekt, więc ich tworzenie constjest bezużyteczne.

Jak zasugerował e.James , możesz użyć znaku NSString * const, który jest stałym wskaźnikiem do pliku NSString. Różni się to nieco od a const NSString *(odpowiednik NSString const *), który jest wskaźnikiem do stałej NSString. Użycie a NSString * constzapobiega ponownemu przypisaniu kPolydo nowego NSStringobiektu.

Adam Rosenfield
źródło
Dobra uwaga na temat używania const. Dlatego wiele klas Objective-C ma warianty „Mutable”.
James
2
Pomyślałem, że to constrównież oznacza, że ​​nie możesz go ponownie przypisać. Chyba się pomyliłem.
Dan Rosenstark
18

Aby uzyskać dostęp z innych klas:

.h

extern NSString * const PolygonNumberOfSidesPrefsKey;

.m

NSString * const PolygonNumberOfSidesPrefsKey = @"PolygonNumberOfSides"

Dostęp tylko do aktualnej klasy:

.m

static NSString * const kPolygonNumberOfSidesPrefsKey = @"PolygonNumberOfSides"
malhal
źródło
5

Sugerowałbym nawet uczynienie tej stałej bardziej opisową. Stała określająca liczbę boków wielokąta może pochodzić z dowolnego miejsca. Jako sugestia, co powiesz na:

kDefaultsPolygonNumberOfSides;

zamiast.

Abizern
źródło