Чому 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.