Почему NSUserDefaults не желает сохранять мой NSDictionary?
Я использую 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
или подобное?
Ответы
Ответ 1
Реквизит Кевину, который предложил распечатать значения, конечно, один из которых имеет тип NSNull
, который не является значением списка свойств. Спасибо!
Решение kludgy для моей проблемы - перебирайте ключи словаря, которые я просто так удобно произвел с помощью 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 [email protected]"" forKey:key];
}
}
Ответ 2
Я обычно архивирую и разархивирую словари при сохранении их по умолчанию.
Таким образом, вам не нужно вручную проверять значения 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"];
Ответ 3
Если вам нужно сохранить;
- данные из пользовательского объекта,
- или массив пользовательских объектов
вы можете использовать методы NSKeyedArchiver. Вы можете проверить leviathan answer для этого метода.
Однако, если вы пытаетесь сохранить,
- словарь, содержащий либо NSString, либо NSNumber (например, словарь, преобразованный из ответа службы JSON),
- или массив этого вида словаря
вам не нужно использовать NSKeyedArchiver. Вы можете использовать пользовательские значения по умолчанию.
В моем случае, когда я извлекал словарь из пользовательских значений по умолчанию, он возвращал нуль, поэтому я думал, что NSUserDefaults не хочет сохранять мой словарь.
Однако он был сохранен, но я использовал неправильный метод getter для извлечения его из пользовательских значений по умолчанию;
[[NSUserDefaults standardUserDefaults] stringForKey:<my_key>]
Пожалуйста, убедитесь, что вы использовали:
[[NSUserDefaults standardUserDefaults] dictionaryForKey:<my_key>]
или
[[NSUserDefaults standardUserDefaults] objectForKey:<my_key>]
прежде чем проверять любую другую возможную причину.