NSUserDefaults - как определить, существует ли ключ


208

Я работаю над небольшим приложением для iPhone, и я использую в NSUserDefaultsкачестве сохранности данных. Нужно только следить за несколькими вещами, такими как некоторые имена и цифры, поэтому я полагаю, что я мог бы также сделать это простым.

Я нашел эту страницу для справки, но я не думаю, что она может ответить на мой вопрос. По сути, я хочу иметь возможность проверить, существует ли уже значение (или ключ) в NSUserDefaultsи затем сделать что-то соответственно.

Некоторые примеры: Приложение запускается, если оно запускается впервые, выдает предупреждение «Добро пожаловать». Чтобы узнать, открылся ли он впервые, он читает UserDefaultsи проверяет.

Пример 2: «Hello [Name]», где Name - это то, что вы ввели. Если вы открыли приложение, а имя отсутствует, оно должно сказать «Hello World». Мне нужно проверить, если вы уже ввели имя и действовать соответственно. Имя будет храниться в NSUserDefaults.

Некоторая помощь здесь? Я действительно ценю это!

Ответы:


382

objectForKey:вернется, nilесли его не существует.


1
Я не думаю, что вы можете хранить примитивный тип данных в NSUserDefaults.
кендер

12
Документы Apple говорят, что «Если логическое значение связано с defaultName в пользовательских значениях по умолчанию, это значение возвращается. В противном случае возвращается NO». Я не думаю, что приведенный выше ответ является правильным для BOOL, вы не можете определить, определен он как НЕТ или не существует. Я думаю, вы должны использовать – dictionaryRepresentationи проверить ключ.
Zekel

40
@zekel Вместо того, чтобы догадываться, я проверил это (на iOS 5.1.1), и он определенно обнаружил, присутствует ли BOOL, независимо от значения этого BOOL. «objectForKey» вернул ноль, когда BOOL отсутствовал, потому что он никогда не был установлен.
DataGraham

8
Если у вас есть BOOL и вы тестируете его с помощью boolForKey, то @zekel прав, вы получаете ДА или НЕТ. Если вы протестируете его с помощью objectForKey (как следует из ответа), вы получите nil, если ключ не установлен.
Джузеппе Гарассино

2
Это больше не работает, по крайней мере, с iOS 6.1 Simulator. objectForKey возвращает то же значение, если его нет и если он присутствует с BOOL из NO. Решение i.jameelkhan работает
lschult2

98

Как упомянуто выше, это не будет работать для примитивных типов, где 0 / NO может быть допустимым значением. Я использую этот код.

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

    NSLog(@"mykey found");
}

Это спасло меня. Спасибо!
BalestraPatrick

Это правильный ответ при работе с такими примитивами, как BOOL. Он будет точно различать NOи не устанавливать, в отличие от objectForKey:.
devios1

@ devios1 - если ключ отсутствует, objectForKey:будет возвращен nilнезависимо от намерения программиста в конечном итоге сохранить Boolили любой другой тип данных. Когда присутствует примитив, objectForKey:он не возвращается, nilдаже если ключ связан со значением примитива.
Тед Хопп

Это правильный ответ: очевидно, принятый ответ неверен, поскольку objectForKey путает 0 с nil, поэтому он не может работать. Успешно испытано с прошивкой 4.3 до 10.2.1
Chrysotribax

Я знаю, что это старая версия, но, имея только эту информацию, мне нужно только указать, что ссылка «containsObject:» означает только это: объект. НЕ ключ. IOW, в вашем заголовочном файле, если вы определили: #define kMyKey @ "myKey", "containsObject" не ищет "kMyKey", он ищет "myKey". Использование 'kMyKey' всегда будет возвращать 'NO.'
Билл Норман,

55

objectForKey:Метод возвращает , nilесли значение не существует. Вот простой тест IF / THEN, который скажет вам, является ли значение ноль:

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

6

« objectForKey вернет nil, если он не существует. » Он также вернет nil, если он существует, и это либо целое число, либо логическое значение со значением ноль (т. е. FALSE или NO для логического значения).

Я проверил это в симуляторе для 5.1 и 6.1. Это означает, что вы не можете действительно проверить, были ли установлены целые или логические значения, запрашивая «объект». Вы можете избежать неприятностей с целыми числами, если не возражаете относиться к «не установлен», как если бы он был «установлен на ноль».

Люди, которые уже проверяли это, похоже, были одурачены ложным отрицательным аспектом, то есть проверяли это, проверяя, возвращает ли objectForKey ноль, когда вы знаете, что ключ не был установлен, но не замечая, что он также возвращает ноль, если ключ был установлен, но был установлен на НЕТ.

Для моей собственной проблемы, которая привела меня сюда, я просто изменил семантику моего логического значения так, чтобы желаемое значение по умолчанию соответствовало значению NO. Если это не вариант, вам нужно хранить как что-то отличное от логического значения и убедиться, что вы можете определить разницу между ДА, НЕТ и «не установлено».


Я подтвердил это, но есть простое решение; просто используйте новые литералы объекта или упакованное выражение. @0вместо 0, @NOвместо NOили просто @(variable). Читайте о них здесь.
Кака

1
Немного поздно, но в пользу новичков: это неверно. object (forKey) для UserDefault значения целых чисел, установленных на 0, а Bools на false, будут правильно возвращать не ноль. Если вы используете bool (forKey) для проверки, установлено ли значение, вы можете столкнуться с проблемами (потому что, если значение установлено в False, bool (forKey) вернет «false», даже если вы ожидаете «true».)
thecloud_of_unknowing

5

Свифт 3/4:

Вот простое расширение для типов ключей-значений Int / Double / Float / Bool, которые имитируют поведение Optional-return других типов, доступных через UserDefaults.

( Изменить 30 августа 2018 года: обновлен более эффективный синтаксис по предложению Лео.)

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
    }
}

Теперь они более согласованы с другими встроенными методами get (строка, данные и т. Д.). Просто используйте методы get вместо старых.

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
Я бы предпочел return self.object(forKey: key) as? Int, чтобы искать значение только один раз.
Лев

3

Я только что прошел через это, и все ваши ответы помогли мне найти хорошее решение для меня. Я отказался идти по предложенному пути, просто потому, что мне было трудно читать и понимать.

Вот что я сделал. У меня была BOOL, которая переносилась в переменной с именем _talkative.

Когда я устанавливаю свой объект по умолчанию (NSUserDefaults), я устанавливаю его как объект, поскольку затем я могу проверить, был ли он нулевым:

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

Затем, когда я пошел посмотреть, существует ли он, я использовал:

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

Затем я использовал объект как BOOL:

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

Кажется, это работает в моем случае. Это просто придало мне больше визуального смысла.


Это сработало для меня ([по умолчанию boolForKey: @ "
talkative

3

Попробуй эту маленькую крошку:

-(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];
    }
}

Относитесь ко всему как к объекту. Кажется, работает на меня.


3

Быстрая версия, чтобы получить Bool?

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

1
Почему бы не использовать boolForKey? NSUserDefaults.standardUserDefaults().boolForKey(DefaultsIsGiver)
JAL

1
boolForKeyвернется Boolи не Bool?так , если ключ не там вы получите , falseа неnil
Бен

3

Продлите UserDefaultsодин раз, чтобы не копировать и не вставлять это решение:

extension UserDefaults {

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

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

0

В Swift3 я использовал таким образом

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

Ответ велик , если вы хотите использовать , что несколько раз.

Я надеюсь, что это помогает :)


-1

Swift 3.0

if NSUserDefaults.standardUserDefaults().dictionaryRepresentation().contains({ $0.0 == "Your_Comparison_Key" }){
                    result = NSUserDefaults.standardUserDefaults().objectForKey(self.ticketDetail.ticket_id) as! String
                }
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.