Почему NSUserDefaults не желает сохранять мой NSDictionary? - PullRequest
14 голосов
/ 24 февраля 2012

Я использую KVC для сериализации NSObject и пытаюсь сохранить его в NSUserDefaults, что дает мне Attempt to insert non-property value, когда я пытаюсь сохранить свои NSDictionary.

свойства рассматриваемого объекта, MyClass:

@interface MyClass : NSObject
@property (copy,nonatomic) NSNumber* value1;
@property (copy,nonatomic) NSNumber* value2;
@property (copy,nonatomic) NSString* value3;
@property (copy,nonatomic) NSString* value4;
@property (copy,nonatomic) NSString* value5;
@property (copy,nonatomic) NSString* value6;
@property (copy,nonatomic) NSString* value7;
@property (copy,nonatomic) NSString* value8;
@end

Когда пришло время сохранить MyClass, это происходит здесь:

-(void)saveMyClass
{
  NSArray* keys = [NSArray arrayWithObjects:
                @"value1",
                @"value2",
                @"value3",
                @"value4",
                @"value5",
                @"value6",
                @"value7",
                @"value8",
                nil];
  NSDictionary* dict = [self dictionaryWithValuesForKeys:keys];
  for( id key in [dict allKeys] )
  {
    NSLog(@"%@ %@",[key class],[[dict objectForKey:key] class]);
  }
  NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
  [defaults setObject:dict forKey:[NSString stringWithString:kMyClassKey]];
  [defaults synchronize];
}

, который производит этот вывод:

2012-02-23 19:35:27.518 MyApp[10230:40b] __NSCFConstantString __NSCFNumber
2012-02-23 19:35:27.519 MyApp[10230:40b] __NSCFConstantString __NSCFNumber
2012-02-23 19:35:27.519 MyApp[10230:40b] __NSCFConstantString __NSCFString
2012-02-23 19:35:27.519 MyApp[10230:40b] __NSCFConstantString __NSCFString
2012-02-23 19:35:27.520 MyApp[10230:40b] __NSCFConstantString __NSCFString
2012-02-23 19:35:27.520 MyApp[10230:40b] __NSCFConstantString __NSCFString
2012-02-23 19:35:27.520 MyApp[10230:40b] __NSCFConstantString __NSCFString
2012-02-23 19:35:27.520 MyApp[10230:40b] __NSCFConstantString NSNull
2012-02-23 18:38:48.489 MyApp[9709:40b] *** -[NSUserDefaults setObject:forKey:]: Attempt to insert non-property value '{
    value1 = "http://www.google.com";
    value2 = "MyClassData";
    value3 = 8;
    value4 = "<null>";
    value5 = "http://www.google.com";
    value6 = 1;
    value7 = "http://www.google.com";
    value8 = 4SY8KcTSGeKuKs7s;
}' of class '__NSCFDictionary'.  Note that dictionaries and arrays in property lists must also contain only property values.`

Как видите, все объекты в dict являются значениями списка свойств, а все его ключи NSString*.Какие мелочи мне не хватает, чтобы выполнить это?Или я должен сдаться и использовать writeToFile или подобное?

Ответы [ 3 ]

21 голосов
/ 24 февраля 2012

Подает запрос Кевину, который предложил напечатать значения, одно из которых, конечно, имеет тип NSNull, который не является значением списка свойств. Спасибо!

Глупое решение моей проблемы - переберите ключи словаря, которые я только что так удобно создал с помощью dictionaryWithValuesForKeys, и удалите ключи типа NSNull. Вздох

NSMutableDictionary* dict = [NSMutableDictionary dictionaryWithDictionary:[self dictionaryWithValuesForKeys:keys]];
for( id key in [dict allKeys] )
{
    if( [[dict valueForKey:key] isKindOfClass:[NSNull class]] )
    {
        // doesn't work - values that are entered will never be removed from NSUserDefaults
        //[dict removeObjectForKey:key];
        [dict setObject@"" forKey:key];
    }
}
20 голосов
/ 25 июня 2013

Я обычно архивирую и разархивирую словари при сохранении их по умолчанию для пользователя. Таким образом, вам не нужно вручную проверять NSNull значения.

Просто добавьте следующие два метода в ваш код. Потенциально в категории.

- (BOOL)archive:(NSDictionary *)dict withKey:(NSString *)key {
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    NSData *data = nil;
    if (dict) {
        data = [NSKeyedArchiver archivedDataWithRootObject:dict];
    }
    [defaults setObject:data forKey:key];
    return [defaults synchronize];
}

- (NSDictionary *)unarchiveForKey:(NSString *)key {
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    NSData *data = [defaults objectForKey:key];
    NSDictionary *userDict = nil;
    if (data) {
        userDict = [NSKeyedUnarchiver unarchiveObjectWithData:data];
    }
    return userDict;
}

Затем вы можете заархивировать любой словарь, как этот (при условии, что метод доступен в классе):

NSDictionary *dict = ...;
[self archive:dict withKey:@"a key of your choice"];

и получить его позже снова так:

NSDictionary *dict = [self unarchiveForKey:@"a key of your choice"];
0 голосов
/ 16 марта 2015

Если вам нужно хранить;

  • данные из пользовательского объекта,
  • или массив пользовательских объектов

вы можете использовать методы NSKeyedArchiver. Вы можете проверить ответ Левиафана для этого метода.


Однако, если вы пытаетесь сохранить;

  • словарь, содержащий NSString или NSNumber (как словарь, преобразованный из ответа службы JSON),
  • или массив такого рода словарей

вам не нужно использовать NSKeyedArchiver. Вы можете использовать пользовательские настройки по умолчанию.


В моем случае, когда я извлекал словарь из настроек пользователя по умолчанию, он возвращал ноль, поэтому я подумал, что NSUserDefaults не хочет сохранять свой словарь.

Тем не менее, он был сохранен, но я использовал неправильный метод получения, чтобы извлечь его из значений по умолчанию пользователя;

[[NSUserDefaults standardUserDefaults] stringForKey:<my_key>]

Пожалуйста, убедитесь, что вы использовали либо

[[NSUserDefaults standardUserDefaults] dictionaryForKey:<my_key>]

или *; 1037 *

[[NSUserDefaults standardUserDefaults] objectForKey:<my_key>]

перед проверкой любой другой возможной причины.

...