NSUserDefaults - Come sapere se esiste una chiave


208

Sto lavorando su una piccola app per iPhone e sto usando NSUserDefaultscome persistenza dei miei dati. Deve solo tenere traccia di alcune cose, come alcuni nomi e alcuni numeri, quindi immagino che potrei anche mantenerlo semplice.

Ho trovato questa pagina come riferimento, ma non credo che possa rispondere alla mia domanda. Fondamentalmente, voglio essere in grado di verificare se un valore (o una chiave) esiste già in NSUserDefaultse quindi fare qualcosa di conseguenza.

Alcuni esempi: l'app si avvia, se è la prima volta che si avvia emette un avviso che dice benvenuto. Per sapere se è la prima volta che apre, legge UserDefaultse verifica.

Esempio 2: dice "Ciao [Nome]", dove Nome è qualcosa che hai inserito. Se hai aperto l'app e non esiste un nome, dovrebbe essere visualizzato "Hello World". Devo verificare se hai già inserito un nome e agire di conseguenza. Il nome verrebbe memorizzato inNSUserDefaults .

Qualche aiuto qui? Lo apprezzerei davvero!

Risposte:


382

objectForKey:tornerà nilse non esiste.


1
Non credo che sia possibile archiviare un tipo di dati primitivo in NSUserDefaults.
kender,

12
I documenti di Apple affermano che "Se un valore booleano è associato a defaultName nelle impostazioni predefinite dell'utente, quel valore viene restituito. In caso contrario, viene restituito NO". Non credo che la risposta sopra sia corretta per i BOOL, non puoi determinare se è definito NO o non esiste. Penso che dovresti usare – dictionaryRepresentatione verificare la chiave.
zekel,

40
@zekel Invece di indovinarlo, l'ho testato (su iOS 5.1.1) e ha sicuramente rilevato la presenza o meno di un BOOL, indipendentemente dal valore di detto BOOL. "objectForKey" ha restituito zero quando BOOL non era presente perché non era mai stato impostato.
DataGraham,

8
Se hai un BOOL e testalo con boolForKey, allora @zekel ha ragione, ottieni SÌ o NO. Se lo provi con objectForKey (come suggerisce la risposta) otterrai zero se la chiave non è impostata.
Giuseppe Garassino,

2
Questo non funziona più almeno a partire dal simulatore iOS 6.1. objectForKey restituisce lo stesso valore se non è presente e se è presente con un valore BOOL di NO. La soluzione di i.jameelkhan funziona
lschult2

98

Come accennato in precedenza, non funzionerà con tipi primitivi in ​​cui 0 / NO potrebbe essere un valore valido. Sto usando questo codice.

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

    NSLog(@"mykey found");
}

Questo mi ha salvato. Grazie!
BalestraPatrick,

Questa è la risposta corretta quando si tratta di primitivi come BOOL. Distinguerà accuratamente tra NOe non impostato, a differenza objectForKey:.
devios1,

@ devios1 - Se manca la chiave, objectForKey:tornerà a nilprescindere dall'intenzione del programmatore di memorizzare eventualmente uno Boolo altri tipi di dati. Quando è presente una primitiva, objectForKey:non ritorna nilanche se la chiave è associata a un valore di primitiva.
Ted Hopp,

Questa è la risposta giusta: ovviamente, la risposta accettata è sbagliata poiché objectForKey confonde 0 con zero, quindi non può funzionare. Testato con successo da iOS 4.3 a 10.2.1
Chrysotribax

So che questo è vecchio, ma avendo solo ora bisogno di queste informazioni, devo sottolineare che il riferimento 'IncludesObject:' significa proprio questo: l'oggetto. NON la chiave. IOW, nel tuo file di intestazione se hai definito: #define kMyKey @ "myKey" l'oggetto "contieneOggetto" non sta cercando "kMyKey", ma sta cercando "myKey". L'uso di "kMyKey" restituirà sempre "NO".
Bill Norman,

55

Il objectForKey:metodo restituirà nilse il valore non esiste. Ecco un semplice test IF / THEN che ti dirà se il valore è zero:

if([[NSUserDefaults standardUserDefaults] objectForKey:@"YOUR_KEY"] != nil) {
    ...
}

6

" objectForKey restituirà zero se non esiste. " Restituirà inoltre zero se esiste ed è un numero intero o un valore booleano con un valore pari a zero (ovvero FALSO o NO per il valore booleano).

Ho provato questo nel simulatore per 5.1 e 6.1. Ciò significa che non è possibile verificare realmente se sono stati impostati numeri interi o booleani chiedendo "l'oggetto". Puoi farcela con gli interi se non ti dispiace trattare "non impostato" come se fosse "impostato su zero".

Le persone che hanno già provato questo sembrano essere state ingannate dall'aspetto falso negativo, vale a dire testarlo vedendo se objectForKey restituisce zero quando si sa che la chiave non è stata impostata ma non si nota che restituisce zero se la chiave è stata impostato ma è stato impostato su NO.

Per il mio problema, che mi ha inviato qui, ho appena finito di cambiare la semantica del mio booleano in modo che il mio default desiderato fosse in congruenza con il valore impostato su NO. Se questa non è un'opzione, dovrai archiviarla come qualcosa di diverso da un valore booleano e assicurarti di poter dire la differenza tra SÌ, NO e "non impostato".


L'ho confermato, ma esiste una soluzione semplice; basta usare i nuovi letterali di oggetti o un'espressione in scatola. @0invece di 0, @NOinvece di NOo semplicemente @(variable). Leggi su di loro qui.
kaka,

1
Un po 'in ritardo, ma a beneficio dei neofiti: questo non è corretto. L'oggetto (forKey) su UserDefault valori di numeri interi impostati su 0 e valori booleani impostati su false restituiranno correttamente non nulli. Se si utilizza bool (forKey) per verificare se è impostato un valore, è possibile che si verifichino problemi (perché se il valore è impostato su False, bool (forKey) restituirà "false", anche se ci si aspetta "true".)
thecloud_of_unknowing

5

Swift 3/4:

Ecco una semplice estensione per i tipi di valori-chiave Int / Double / Float / Bool che imitano il comportamento di ritorno opzionale degli altri tipi a cui si accede tramite UserDefaults.

( Modifica il 30 agosto 2018: aggiornato con una sintassi più efficiente dal suggerimento di 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
    }
}

Ora sono più coerenti con gli altri metodi get incorporati (stringa, dati, ecc.). Basta usare i metodi get al posto di quelli vecchi.

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

2
Preferirei return self.object(forKey: key) as? Intper cercare valore una sola volta.
Leone,

3

Ho appena superato questo e tutto il resto tue risposte mi hanno aiutato a trovare una buona soluzione, per me. Ho resistito seguendo il percorso suggerito, solo perché mi è stato difficile leggere e comprendere.

Ecco cosa ho fatto. Avevo un BOOL portato in giro in una variabile chiamata "_talkative".

Quando ho impostato il mio oggetto predefinito (NSUserDefaults), l'ho impostato come oggetto, in quanto ho potuto quindi verificare se fosse nullo:

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

Quindi quando sono andato a vedere se esisteva, ho usato:

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

Quindi ho usato l'oggetto come BOOL:

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

Questo sembra funzionare nel mio caso. Ha solo avuto più senso visivo per me.


Questo ha funzionato per me ([default boolForKey: @ "loquace"]
Vineesh TP

3

Prova questa piccola 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];
    }
}

Tratta tutto come un oggetto. Sembra funzionare per me.


3

Versione rapida per ottenere Bool?

NSUserDefaults.standardUserDefaults().objectForKey(DefaultsIsGiver) as? Bool

1
Perché non usare boolForKey? NSUserDefaults.standardUserDefaults().boolForKey(DefaultsIsGiver)
JAL

1
boolForKeytornerà Boole no Bool?, quindi se la chiave non è lì otterrai falsee nonnil
Ben

3

Estendi UserDefaultsuna volta per non copiare e incollare questa soluzione:

extension UserDefaults {

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

// Example
UserDefaults.standard.hasValue(forKey: "username")

0

In Swift3, l'ho usato in questo modo

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

La risposta è ottima se la usi più volte.

Spero possa essere d'aiuto :)


-1

Swift 3.0

if NSUserDefaults.standardUserDefaults().dictionaryRepresentation().contains({ $0.0 == "Your_Comparison_Key" }){
                    result = NSUserDefaults.standardUserDefaults().objectForKey(self.ticketDetail.ticket_id) as! String
                }
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.