NSUserDefaults - Як визначити, чи існує ключ


208

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

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

Деякі приклади. Додаток запускається, якщо це перший раз, коли він запускається, він видає попередження, вітаючи привітання. Щоб сказати, чи це вперше він відкрився, він читає UserDefaultsта перевіряє.

Приклад 2: Там написано "Привіт [Ім'я]", де Ім'я - це те, що ви ввели. Якщо ви відкрили додаток і немає його імені, він повинен сказати "Hello World". Мені потрібно перевірити, чи ви вже ввели ім’я, і діяти відповідно. Ім'я буде збережено в NSUserDefaults.

Деякі допомоги тут? Я б дуже цінував це!

Відповіді:


382

objectForKey:повернеться, nilякщо його не існує.


1
Я не думаю, що ви можете зберігати примітивний тип даних у NSUserDefaults.
кендер

12
Документи Apple говорять, що "Якщо булеве значення пов'язане з defaultName у налаштуваннях користувача за замовчуванням, це значення повертається. Інакше НІ не повертається". Я не думаю, що вищевказана відповідь є правильною для BOOL, ви не можете визначити, чи визначено це НІ чи не існує. Я думаю, вам доведеться використовувати – dictionaryRepresentationта перевірити ключ.
zekel

40
@zekel Я замість того, щоб здогадуватися, я протестував це (на iOS 5.1.1), і він напевно виявив наявність BOOL чи ні, незалежно від значення цього BOOL. "objectForKey" повернув нуль, коли BOOL не було, оскільки він ніколи не був встановлений.
DataGraham

8
Якщо у вас є BOOL і протестуйте його за допомогою boolForKey, то @zekel вірно, ви отримаєте ТАК чи НІ. Якщо ви перевірите це за допомогою objectForKey (як підказує відповідь), ви отримаєте нуль, якщо ключ не встановлений.
Джузеппе Гарассіно

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 з нулем, тому він не може працювати. Успішно випробувано на iOS 4.3 до 10.2.1
Chrysotribax

Я знаю, що це давнє, але, маючи лише зараз потрібну цю інформацію, мені потрібно зазначити, що посилання 'міститьObject:' означає саме це: об'єкт. НЕ ключ. IOW, у вашому файлі заголовка, якщо ви визначили: #define kMyKey @ "myKey", "міститьObject" не шукає "kMyKey", він шукає "myKey". Використання "kMyKey" завжди поверне "НІ".
Білл Норман

55

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

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

6

" objectForKey поверне нуль, якщо його не існує. " Він також поверне нуль, якщо він існує, і це або ціле число, або булеве значення зі значенням нуля (тобто FALSE або NO для булевого).

Я перевірив це в тренажері для 5.1 і 6.1. Це означає, що ви не можете реально перевірити встановлені цілі чи булеві числа, запитуючи "об'єкт". Ви можете розібратися з цілими числами, якщо ви не заперечуєте, щоб "не встановити" так, як якщо б це було "нуль".

Люди, які вже тестували це, здається, були обдурені помилковим негативним аспектом, тобто тестуючи це, побачивши, що objectForKey повертає нуль, коли ви знаєте, що ключ не встановлений, але не помічаючи, що він також повертає нуль, якщо ключ був set, але було встановлено як NO.

З моєї власної проблеми, яка мене надіслала сюди, я щойно змінив семантику свого булевого сигналу, щоб мій бажаний дефолт відповідав значенню NO. Якщо це не варіант, вам потрібно буде зберігати щось інше, ніж булеве, і переконайтесь, що ви можете сказати різницю між ТАК, НІ та "не встановлено".


Я підтвердив це, але є просте рішення; просто використовуйте нові об'єктивні літерали або вираз у коробці. @0замість 0, @NOзамість NO, або просто @(variable). Про них читайте тут.
кака

1
Трохи пізно, але на користь новачкам: це неправильно. об'єкт (forKey) на UserDefault значення цілих чисел, встановлених на 0, і Bools, встановлений на false, буде правильно повернути ненульове значення. Якщо ви використовуєте bool (forKey), щоб перевірити, чи встановлено значення, ви можете зіткнутися з проблемами (тому що якщо для параметра встановлено значення False, bool (forKey) повернеться "false", навіть якщо ви очікуєте "true".)
thecloud_of_unknowing

5

Швидкий 3/4:

Ось просте розширення для типів ключа / значення Int / Double / Float / Bool, що імітують поведінку необов'язкового повернення інших типів, до яких можна отримати доступ через 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 замість старих.

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 )
  {

Тоді я використовував об'єкт як БУЛЬ:

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

Здається, це працює в моєму випадку. Це просто надавало мені більше візуального сенсу.


Це працювало для мене ([за замовчуванням boolForKey: @ "балакучий"]
Vineesh TP

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.