Чому NSUserDefaults не вдалося зберегти NSMutableDictionary в iOS?


145

Я хотів би зберегти NSMutableDictionaryоб’єкт у NSUserDefaults. Ключовим типом NSMutableDictionaryє NSStringтип типу NSArray, який містить перелік об'єктів, які реалізуються NSCoding. На документ, NSStringі NSArrayобидва відповідають NSCoding.

Я отримую цю помилку:

[NSUserDefaults setObject: forKey:]: Спроба вставити значення не властивості .... класу NSCFDictionary.

Відповіді:


230

Я дізнався одну альтернативу, перш ніж зберегти, кодую кореневий об’єкт ( NSArrayоб’єкт) за допомогою NSKeyedArchiver, який закінчується NSData. Потім за допомогою UserDefaults збережіть NSData.

Коли мені потрібні дані, я зачитую NSDataі використовую NSKeyedUnarchiverдля перетворення NSDataназад в об'єкт.

Це трохи громіздко, тому що мені потрібно конвертувати в / з NSDataкожного разу, але це просто працює.

Ось один приклад на запит:

Зберегти:

NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSMutableArray *arr = ... ; // set value
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:arr];
[defaults setObject:data forKey:@"theKey"];
[defaults synchronize];

Завантажте:

NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSData *data = [defaults objectForKey:@"theKey"];
NSArray *arr = [NSKeyedUnarchiver unarchiveObjectWithData:data];

Елемент в масиві реалізується

@interface CommentItem : NSObject<NSCoding> {
    NSString *value;
}

Тоді при реалізації CommentItem, передбачено два способи:

-(void)encodeWithCoder:(NSCoder *)encoder
{
    [encoder encodeObject:value forKey:@"Value"];
}

-(id)initWithCoder:(NSCoder *)decoder
{
    self.value = [decoder decodeObjectForKey:@"Value"];
    return self;
}

У кого є краще рішення?

Дякую всім.


4
Чи є у вас зразок коду, яким ви можете поділитися, я намагався це зробити у публікації (537044), але без радості
— Ентоні Майн

Це добре працювало для мене. Я намагався кодувати NSArray об'єктів NSDictionary, де деякі клавіші не були рядками. Просто за допомогою NSKeyedArchiver та Unarchiver на NSArray позбувся помилки "Спроба вставити значення невластивості".
— Джон Райт

Коли мені потрібно звільнити завантажений об’єкт? Жоден аллок не називався, тож я би припустив, що він авторизований?
— Марк

7
нам може знадобитися додати [синхронізацію за замовчуванням] для економії
— thanhbinh84

Розміщене тут рішення може працювати деякий час, але коли ви вирішите видалити або змінити клас, ви опинитеся в заплутаному стані. Краще рішення - змусити ваш об'єкт записати його стан за допомогою NSNumbers, Strings тощо у словник, а потім зберегти його. Майбутнє доказ.
— Том Андерсен

105

Якщо ви зберігаєте об'єкт у налаштуваннях за замовчуванням користувача, усі об'єкти, рекурсивно, аж донизу, повинні бути об'єктами списку властивостей. Відповідність NSCoding тут нічого не означає NSUserDefaults- автоматично не кодує їх NSData, ви повинні зробити це самостійно. Якщо ваш "список об'єктів, який реалізується NSCoding" означає об'єкти, які не є об'єктами списку властивостей, вам доведеться щось зробити з ними перед збереженням за замовчуванням користувача.

FYI класи список нерухомості є NSDictionary, NSArray, NSString, NSDate, NSData, і NSNumber. Ви можете писати змінні підкласи (наприклад NSMutableDictionary) за налаштуваннями користувачів, але об'єкти, які ви читаєте, завжди будуть незмінні.


61
Я також повинен зазначити, що NSDictionary є лише об'єктом списку властивостей, якщо ключі є NSStrings. Як правило, ви можете використовувати інші об'єкти як ключі словника, але там, де потрібні списки властивостей, вони повинні бути рядками.
— Том Харрінгтон,

Я зіткнувся з тією ж проблемою, коли я хотів зберігати NSURL як об’єкт у NSDictionary і зберігати його в NSUserDefaults. Мені довелося змінити його на NSString, ніж це спрацювало.
— NicTesla

1
Чудово! У моєму випадку я намагався використовувати NSNumber як ключ NSDictionary в налаштуваннях користувачів. Я повинен змінити його на NSString.
— Joey

1
Така відстала поведінка - що призводить до того, що недосвідчені кодери записують всю свою модель даних у словники замість створення належних об'єктів даних? Наскільки страшно може бути для Apple, щоб писати фундаментальні класи, які використовують той факт, що obj-c має самоаналіз та піклується про бокс та unboxing для нас?
— ArtOfWarfare

14

Чи всі ваші ключі у словнику NSStrings? Я думаю, що вони повинні бути для того, щоб зберегти словник до списку властивостей.


1
ключі - NSString. але значення - NSArray, елементом якого є індивідуальний клас, який реалізує NSC-кодування. Здається, що рішення не працює, оскільки UserDefaults підтримує лише 6 типів класу, не враховувати NSCoding.
— BlueDolphin

8

Найпростіший відповідь:

NSDictionaryє лише об'єктом plist , якщо ключі - NSStrings . Отже, зберігайте "Ключ", як NSStringі в stringWithFormat.


Рішення:

NSString *key = [NSString stringWithFormat:@"%@",[dictionary valueForKey:@"Key"]];

Переваги:

  1. Це додасть значення String-Value .
  2. Він додасть значення " Порожнє значення", коли значення Вашої змінної NULL.

2
У мене була подібна проблема: мої ключі були NSNumbers, які, очевидно, не дозволені в клавішах словацьких словників.
— Марк Полі

5

Ви розглядали питання про виконання NSCodingпротоколу? Це дозволить вам кодувати та декодувати на iPhone двома простими методами, реалізованими за допомогою NSCoding. Спочатку вам потрібно буде додати NSCodingсвій клас у свій клас.

Ось приклад:

Це у файлі .h

@interface GameContent : NSObject <NSCoding>

Тоді вам потрібно буде реалізувати два методи протоколу кодування NSC.

    - (id) initWithCoder: (NSCoder *)coder
    {
        if (self = [super init])
        {
               [self setFoundHotSpots:[coder decodeObjectForKey:@"foundHotSpots"]];
        }
        return self;
    }

    - (void) encodeWithCoder: (NSCoder *)coder
    {
           [coder encodeObject:foundHotSpots forKey:@"foundHotSpots"];
    }

Перегляньте документацію на NSCoder для отримання додаткової інформації. Це дуже зручно для моїх проектів, коли мені потрібно зберегти стан програми на iPhone, якщо програма закрита і повернути її до стану, коли її знову ввімкнено.

Ключ полягає в тому, щоб додати протокол до інтерфейсу, а потім реалізувати два способи, що входять до складу NSCoding.

Я сподіваюся, що це допомагає!


0

Немає кращого рішення. Іншим варіантом було б просто зберегти закодований об'єкт на диск, але це робиться те саме. Вони обоє закінчуються NSData, який розшифровується, коли ви хочете його повернути.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.