Каков наилучший способ сохранить NSMutableArray в NSUserDefaults?

У меня есть пользовательский объект с именем Event, определенный следующим образом:

#import <Foundation/Foundation.h>


@interface Occasion : NSObject {

NSString *_title;
NSDate *_date;
NSString *_imagePath;    

}

@property (nonatomic, retain) NSString *title;
@property (nonatomic, retain) NSDate *date;
@property (nonatomic, retain) NSString *imagePath;

Теперь у меня есть NSMutableArray случаев, которые я хочу сохранить в NSUserDefaults. Я знаю, что это невозможно прямолинейно, поэтому мне интересно, какой самый простой способ сделать это? Если сериализация — это ответ, то как? Потому что я читал документы, но не мог полностью понять, как это работает.


person Ali    schedule 12.10.2011    source источник


Ответы (3)


Вы должны использовать что-то вроде NSKeyedArchiver для сериализации массива в NSData, сохранить его в NSUserDefaults, а затем использовать NSKeyedUnarchiver для его десериализации позже:

NSData *serialized = [NSKeyedArchiver archivedDataWithRootObject:myArray];
[[NSUserDefaults standardUserDefaults] setObject:serialized forKey:@"myKey"];

//...

NSData *serialized = [[NSUserDefaults standardUserDefaults] objectForKey:@"myKey"];
NSArray *myArray = [NSKeyedUnarchiver unarchiveObjectWithData:serialized];

Вам нужно будет реализовать протокол NSCoding в вашем классе Occasion и правильно сохранить различные свойства, чтобы все работало правильно. Дополнительные сведения см. в Руководстве по программированию архивов и сериализации. Для этого потребуется не более нескольких строк кода. Что-то типа:

- (void)encodeWithCoder:(NSCoder *)coder {
    [super encodeWithCoder:coder];

    [coder encodeObject:_title forKey:@"_title"];
    [coder encodeObject:_date forKey:@"_date"];
    [coder encodeObject:_imagePath forKey:@"_imagePath"];
}

- (id)initWithCoder:(NSCoder *)coder {
    self = [super initWithCoder:coder];

    _title = [[coder decodeObjectForKey:@"_title"] retain];
    _date = [[coder decodeObjectForKey:@"_date"] retain];
    _imagePath = [[coder decodeObjectForKey:@"_imagePath"] retain];

    return self;
}
person Mike Weller    schedule 12.10.2011
comment
Спасибо за вашу помощь. Идея мне теперь ясна. Последний вопрос: на шаге (NSArray *myArray = [NSKeyedUnarchiver unarchiveObjectWithData:serialized];) я получил бы NSArray, а исходный массив — NSMutableArray. Так должен ли я использовать что-то вроде [copy mutable]? - person Ali; 13.10.2011
comment
Если он возвращает вам стандартный NSArray, используйте [array mutableCopy], чтобы получить изменяемую версию. - person Mike Weller; 13.10.2011

NSUserDefaults предназначен для пользовательских настроек, а не для хранения данных приложения. Используйте CoreData или сериализуйте объекты. в каталог документов. Вам нужно, чтобы ваш класс реализовал протокол NSCoding, чтобы он работал.

1) Реализовать NSCoding в Occasion.h

@interface Occasion : NSObject <NSCoding>

2) Реализовать протокол в Occasion.m

- (id)initWithCoder:(NSCoder *)aDecoder {

    if (self = [super init]) {

        self.title = [aDecoder decodeObjectForKey:@"title"];
        self.date = [aDecoder decodeObjectForKey:@"date"];
        self.imagePath = [aDecoder decodeObjectForKey:@"imagePath"];

    }            
    return self;
}

- (void)encodeWithCoder:(NSCoder *)aCoder {

    [aCoder encodeObject:title forKey:@"title"];
    [aCoder encodeObject:date forKey:@"date"];
    [aCoder encodeObject:imagePath forKey:@"imagePath"];
}

3) Архивировать данные в файл в каталоге документов

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
                                    NSUserDomainMask, YES);
NSString *documentsPath = [paths objectAtIndex:0];
NSString *path= [documentsPath stringByAppendingPathComponent:@“occasions”];
[NSKeyedArchiver archiveRootObject:occasions toFile:path];

4) Разархивировать...

NSMutableArray *occasions = [NSKeyedUnarchiver unarchiveObjectWithFile:path];
person Jason Harwig    schedule 12.10.2011
comment
Спасибо за ответ. Мой другой вопрос: у меня уже есть 2 метода в объекте Event с именем: (- (id) init AND - (id) initWithTitle: (NSString *) title date: (NSDate *) date imagePath: (NSString *) imagePath) Теперь должен ли я отменить эти 2 метода и вместо этого использовать initWithCoder и encodeWithCoder всякий раз, когда я хочу создавать новые экземпляры случая? - person Ali; 13.10.2011
comment
Нет, если вы хотите создать объекты, используйте init или initWithTitle:date:imagePath. Методы кодера предназначены только для сериализации. - person Jason Harwig; 13.10.2011

Вы можете реализовать NSCoding в Occasion.

Затем вы используете [NSKeyedArchiver archivedDataWithRootObject:myArray] для создания объекта NSData из массива. Вы можете поместить это в пользовательские настройки по умолчанию.

person Nikolai Ruhe    schedule 12.10.2011