несовместимое поведение по умолчанию (NSUserDefaults) в настройках симулятора iPhone по умолчанию

У меня были очень противоречивые результаты при разработке приложения для iPhone и попытке сохранить настройки с помощью стандартного механизма NSUserDefaults. Я использую код почти прямо из Поваренной книги разработчиков iPhone Эрики Садун (кстати, фантастическая книга), это выглядит так:

(void) updateDefaults
{
    NSMutableArray *spells =  [[NSMutableArray alloc] init];
    NSMutableArray *locs = [[NSMutableArray alloc] init];

    for (DragView *dv in [boardView subviews]) 
    {
        [spells addObject:[dv whichSpell]];
        [locs addObject:NSStringFromCGRect([dv frame])]; 
    }

    [[NSUserDefaults standardUserDefaults] setObject:spells forKey:@"spells"];
    [[NSUserDefaults standardUserDefaults] setObject:locs forKey:@"locs"];
    [[NSUserDefaults standardUserDefaults] synchronize];

    [spells release];
    [locs release];
}

Значения сохраняются, иногда ... и иногда восстанавливаются. Я не могу точно сказать, что работает, а что нет.

Есть ли у кого-нибудь еще подобный опыт? Есть предложения по поводу того, что может заставить его работать? Является ли метод синхронизации лучшим способом принудительной записи на диск и сохранения значений, или есть что-то лучшее (как для производства, так и для симулятора).

Спасибо Райан


person ryan.scott    schedule 24.01.2009    source источник


Ответы (2)


Вы должны использовать NSKeyedArchiver для сохранения ваших массивов, например:

[[NSUserDefaults standardUserDefaults] setObject:[NSKeyedArchiver archivedDataWithRootObject:spells] forKey:@"spells"];
[[NSUserDefaults standardUserDefaults] setObject:[NSKeyedArchiver archivedDataWithRootObject:locs] forKey:@"locs"];
[[NSUserDefaults standardUserDefaults] synchronize];

Вы также должны убедиться, что ваш класс заклинаний реализует протокол NSCoding (encodeWithCoder: и initWithCoder :), если это настраиваемый класс. Похоже, что ваши locs - это NSStrings, которые прекрасно заархивируются.

Вам также нужно будет сделать что-то вроде

NSData *dataRepresentingSavedSpells = [currentDefaults objectForKey:@"spells"];
if (dataRepresentingSavedSpells != nil)
{
    NSArray *oldSpells = [NSKeyedUnarchiver unarchiveObjectWithData:dataRepresentingSavedSpells];
}

Чтобы загрузить старые значения из значений по умолчанию.

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

person Brad Larson    schedule 24.01.2009
comment
Спасибо. Я немного смущен тем, почему мне нужно использовать NSKeyedArchiver для хранения массива ... будет ли стандартный setObject не работать точно или в достаточной степени по какой-либо причине? Раньше я не сталкивался с NSKeyedArchiver, поэтому я не знаком ни с чем, кроме вступительного абзаца из документации. - person ryan.scott; 19.02.2009
comment
Я считаю, что вам нужно использовать его, чтобы убедиться, что ваши собственные классы, совместимые с NSCoding, правильно заархивированы. Я думаю, что setObject работает только с массивами поддерживаемых объектов. Хотя я мог ошибаться. - person Brad Larson; 20.02.2009

В Mac OS X и, вероятно, также в iPhone OS база данных по умолчанию может содержать только объекты списка свойств: NSString, NSNumber, NSArray, NSDictionary и NSData.

Из вашего фрагмента кода не ясно, какой тип заклинания, но если это не одно из вышеперечисленных, оно не будет сохранено в базе данных по умолчанию. Возможны следующие варианты:

  1. Напишите метод, который преобразует ваш объект в представление plist и обратно. Другими словами, метод для создания NSDictionary из вашего объекта и другой метод для инициализации объекта с помощью NSDictionary. Вы также можете сохранить его как NSArray или NSString.
  2. Сделайте так, чтобы ваш объект реализовал протокол NSCoding, а затем использовал NSKeyedArchiver для преобразования объекта в объект NSData и NSKeyedUnarchiver для его обратного преобразования.
person benzado    schedule 04.02.2009
comment
да, в какой-то момент я понял, что мой собственный класс не поддерживается, и выбрал версию вашего варианта №1. У меня есть каноническое строковое представление объекта Spell, которое я использую для динамического воссоздания объекта при загрузке. позже мне нужно будет пойти по маршруту №2, но пока нет. - person ryan.scott; 19.02.2009