NSUserDefaults - Jak stwierdzić, czy klucz istnieje

208

Pracuję nad małą aplikacją na iPhone'a i używam jej NSUserDefaultsdo przechowywania danych. Musi tylko śledzić kilka rzeczy, takich jak niektóre nazwiska i niektóre liczby, więc myślę, że równie dobrze mogę to uprościć.

Znalazłem tę stronę w celach informacyjnych, ale nie sądzę, żeby mogła odpowiedzieć na moje pytanie. Zasadniczo chcę być w stanie sprawdzić, czy wartość (lub klucz) już istnieje w, NSUserDefaultsa następnie zrobić coś odpowiednio.

Kilka przykładów: aplikacja uruchamia się, jeśli uruchamia się po raz pierwszy, wyświetla komunikat ostrzegawczy powitania. Aby stwierdzić, czy po raz pierwszy został otwarty, czyta UserDefaultsi sprawdza.

Przykład 2: Napisano „Cześć [Nazwa]”, gdzie Nazwa to coś, co wpisałeś. Jeśli otworzyłeś aplikację i nie ma nazwy, powinna ona brzmieć „Hello World”. Muszę sprawdzić, czy już wpisałeś nazwisko i podjąć odpowiednie działania. Nazwa będzie przechowywana w NSUserDefaults.

Jakaś pomoc tutaj? Byłbym bardzo wdzięczny!

Ethan Mick
źródło

Odpowiedzi:

382

objectForKey:wróci, niljeśli nie istnieje.

jspcal
źródło
1
Nie sądzę, że można przechowywać prymitywny typ danych w NSUserDefaults.
kender
12
Dokumenty Apple'a mówią, że „Jeśli wartość logiczna jest powiązana z wartością defaultName w wartości domyślnej użytkownika, ta wartość jest zwracana. W przeciwnym razie zwracane jest NIE”. Nie sądzę, aby powyższa odpowiedź była poprawna dla BOOL-ów, nie można ustalić, czy jest zdefiniowana NIE, czy nie istnieje. Myślę, że będziesz musiał użyć – dictionaryRepresentationi sprawdzić klucz.
zekel
40
@zekel Zamiast zgadywania, przetestowałem to (na iOS 5.1.1) i zdecydowanie wykryłem, czy BOOL był obecny, niezależnie od wartości tego BOOL. „objectForKey” zwrócił zero, gdy BOOL nie był obecny, ponieważ nigdy nie został ustawiony.
DataGraham
8
Jeśli masz BOOL i przetestujesz go za pomocą boolForKey, to @zekel ma rację, otrzymasz TAK lub NIE. Jeśli przetestujesz go za pomocą objectForKey (jak sugeruje odpowiedź), otrzymasz zero, jeśli klucz nie jest ustawiony.
Giuseppe Garassino,
2
To już nie działa, co najmniej w przypadku symulatora iOS 6.1. objectForKey zwraca tę samą wartość, jeśli nie jest obecna i jeśli zawiera BOOL o wartości NO. Rozwiązanie i.jameelkhan działa
lschult2
98

Jak wspomniano powyżej, nie będzie działać dla typów pierwotnych, w których 0 / NO może być prawidłową wartością. Używam tego kodu.

NSUserDefaults *defaults= [NSUserDefaults standardUserDefaults];
if([[[defaults dictionaryRepresentation] allKeys] containsObject:@"mykey"]){

    NSLog(@"mykey found");
}
i.jameelkhan
źródło
To mnie uratowało. Dzięki!
BalestraPatrick
To poprawna odpowiedź w przypadku takich prymitywów jak BOOL. W odróżnieniu od tego dokładnie rozróżnia NOi nie ustawia objectForKey:.
devios1
@ devios1 - Jeśli klucza brakuje, objectForKey:zwróci nilbez względu na zamiar programisty, aby ostatecznie zapisać Booljakiś inny typ danych. Gdy prymityw jest obecny, objectForKey:nie zwraca, nilnawet jeśli klucz jest powiązany z pierwotną wartością.
Ted Hopp,
Oto poprawna odpowiedź: oczywiście zaakceptowana odpowiedź jest błędna, ponieważ objectForKey myli 0 z zero, więc nie może działać. Pomyślnie przetestowano z iOS 4.3 do 10.2.1
Chrysotribax
Wiem, że to stare, ale właśnie teraz potrzebowałem tych informacji, muszę zaznaczyć, że odwołanie „zawieraObiekt:” oznacza po prostu: obiekt. NIE klucz. IOW, w pliku nagłówkowym, jeśli zdefiniowałeś: # zdefiniować kMyKey @ „myKey”, „zawieraObiekt” nie szuka „kMyKey”, lecz „myKey”. Użycie „kMyKey” zawsze zwróci „NIE”.
Bill Norman
55

objectForKey:Metoda powróci nil, jeśli wartość nie istnieje. Oto prosty test IF / THEN, który pokaże, czy wartość wynosi zero:

if([[NSUserDefaults standardUserDefaults] objectForKey:@"YOUR_KEY"] != nil) {
    ...
}
mirap
źródło
6

objectForKey zwróci zero, jeśli nie istnieje. ” Zwróci również zero, jeśli istnieje i jest albo liczbą całkowitą, albo wartością logiczną o wartości zero (tj. FAŁSZ lub NIE dla wartości logicznej).

Przetestowałem to w symulatorze zarówno dla wersji 5.1, jak i 6.1. Oznacza to, że nie można tak naprawdę sprawdzić, czy zostały ustawione liczby całkowite lub logiczne, prosząc o „obiekt”. Możesz uniknąć tego dla liczb całkowitych, jeśli nie masz nic przeciwko traktowaniu „nieustawione” tak, jakby było „ustawione na zero”.

Wydaje się, że ludzie, którzy już to przetestowali, zostali oszukani przez fałszywie negatywny aspekt, tj. Testują to, sprawdzając, czy objectForKey zwraca zero, gdy wiesz, że klucz nie został ustawiony, ale nie zauważają, że zwraca również zero, jeśli klucz został ustawiony, ale został ustawiony na NIE.

W przypadku mojego problemu, który mnie tu przysłał, właśnie skończyłem na zmianie semantyki mojego logicznego loginu, tak że moje pożądane domyślne ustawienie było zgodne z wartością ustawioną na NIE. Jeśli nie jest to opcja, musisz zapisać jako coś innego niż wartość logiczna i upewnić się, że możesz odróżnić TAK, NIE i „nie ustawiono”.

JamesKVL
źródło
Potwierdziłem to, ale istnieje łatwe rozwiązanie; wystarczy użyć nowych literałów obiektowych lub wyrażenia w ramce. @0zamiast 0, @NOzamiast NOlub po prostu @(variable). Przeczytaj o nich tutaj.
kaka
1
Trochę za późno, ale na korzyść początkujących: to nieprawda. obiekt (forKey) na wartości UserDefault liczb całkowitych ustawionych na 0, a Bools ustawionych na false, poprawnie zwróci wartość inną niż zero. Jeśli użyjesz bool (forKey) do przetestowania, czy ustawiona jest wartość, możesz napotkać problemy (ponieważ jeśli wartość jest ustawiona na False, bool (forKey) zwróci „false”, nawet jeśli oczekujesz „true”.)
thecloud_of_unknowing
5

Swift 3/4:

Oto proste rozszerzenie dla typów klucz-wartość Int / Double / Float / Bool, które naśladują zachowanie Opcjonalnego powrotu innych typów dostępnych za pośrednictwem funkcji UserDefault.

( Edytuj 30 sierpnia 2018 r .: Zaktualizowano o bardziej wydajną składnię z sugestii Leo).

extension UserDefaults {
    /// Convenience method to wrap the built-in .integer(forKey:) method in an optional returning nil if the key doesn't exist.
    func integerOptional(forKey: String) -> Int? {
        return self.object(forKey: forKey) as? Int
    }
    /// Convenience method to wrap the built-in .double(forKey:) method in an optional returning nil if the key doesn't exist.
    func doubleOptional(forKey: String) -> Double? {
        return self.object(forKey: forKey) as? Double
    }
    /// Convenience method to wrap the built-in .float(forKey:) method in an optional returning nil if the key doesn't exist.
    func floatOptional(forKey: String) -> Float? {
        return self.object(forKey: forKey) as? Float
    }
    /// Convenience method to wrap the built-in .bool(forKey:) method in an optional returning nil if the key doesn't exist.
    func boolOptional(forKey: String) -> Bool? {
        return self.object(forKey: forKey) as? Bool
    }
}

Są teraz bardziej spójne z innymi wbudowanymi metodami pobierania (ciąg, dane itp.). Po prostu użyj metod get zamiast starych.

let AppDefaults = UserDefaults.standard

// assuming the key "Test" does not exist...

// old:
print(AppDefaults.integer(forKey: "Test")) // == 0
// new:
print(AppDefaults.integerOptional(forKey: "Test")) // == nil
stef
źródło
2
Wolałbym return self.object(forKey: key) as? Int, aby wyszukać wartość tylko raz.
Leo
3

Właśnie to przeszedłem i wszystkie twoje odpowiedzi pomogły mi znaleźć dobre rozwiązanie. Oparłem się wytyczeniu trasy, którą zaproponowałem, tylko dlatego, że trudno mi było czytać i rozumieć.

Oto co zrobiłem. Miałem BOOL przenoszony w zmiennej o nazwie „_talkative”.

Kiedy ustawiam mój domyślny obiekt (NSUserDefaults), ustawiam go jako obiekt, ponieważ mogłem następnie przetestować, czy jest to zero:

//converting BOOL to an object so we can check on nil
[defaults setObject:@(_talkative) forKey:@"talkative"];

Potem, gdy poszedłem sprawdzić, czy istnieje, użyłem:

if ([defaults objectForKey:@"talkative"]!=nil )
  {

Następnie użyłem obiektu jako BOOL:

if ([defaults boolForKey:@"talkative"]) {
 ...

To wydaje się działać w moim przypadku. Po prostu miało to dla mnie większy sens wizualny.

James Burns
źródło
To zadziałało dla mnie ([defaults boolForKey: @ "talkative"]
Vineesh TP
3

Wypróbuj ten mały crumpet:

-(void)saveUserSettings{
NSNumber*   value;

value = [NSNumber numberWithFloat:self.sensativity];
[[NSUserDefaults standardUserDefaults] setObject:value forKey:@"sensativity"];
}
-(void)loadUserSettings{
    NSNumber*   value;
    value = [[NSUserDefaults standardUserDefaults] objectForKey:@"sensativity"];
    if(value == nil){
        self.sensativity = 4.0;
    }else{
        self.sensativity = [value floatValue];
    }
}

Traktuj wszystko jak przedmiot. Wydaje się dla mnie pracować.

pepelkod
źródło
3

Szybka wersja do zdobycia Bool?

NSUserDefaults.standardUserDefaults().objectForKey(DefaultsIsGiver) as? Bool
Ben
źródło
1
Dlaczego nie użyć boolForKey? NSUserDefaults.standardUserDefaults().boolForKey(DefaultsIsGiver)
JAL
1
boolForKeywróci Booli nie Bool?, więc jeśli nie będzie klucza, dostaniesz falsei nienil
Ben
3

Przedłuż UserDefaultsraz, aby nie kopiować-wkleić tego rozwiązania:

extension UserDefaults {

    func hasValue(forKey key: String) -> Bool {
        return nil != object(forKey: key)
    }
}

// Example
UserDefaults.standard.hasValue(forKey: "username")
Mbelsky
źródło
0

W Swift3 korzystałem w ten sposób

var hasAddedGeofencesAtleastOnce: Bool {
    get {
        return UserDefaults.standard.object(forKey: "hasAddedGeofencesAtleastOnce") != nil
    }
}

Odpowiedź jest wielki, jeśli są w użyciu, że wiele razy.

Mam nadzieję, że to pomoże :)

Nikhil Manapure
źródło
-1

Swift 3.0

if NSUserDefaults.standardUserDefaults().dictionaryRepresentation().contains({ $0.0 == "Your_Comparison_Key" }){
                    result = NSUserDefaults.standardUserDefaults().objectForKey(self.ticketDetail.ticket_id) as! String
                }
Kiran Jasvanee
źródło