Сохранение массива в файл Plist, содержащий пользовательские объекты iPhone

У меня есть класс XYZ, наследуемый от NSObject, и массив, содержащий объекты XYZ. Мне нужно записать этот массив в файл plist в каталог документов. После попытки [array writeToFile....] я обнаружил, что writeToFile терпит неудачу, потому что массив содержит пользовательские объекты класса XYZ.

Возможным решением этой проблемы является преобразование объектов в NSData. Каковы различные способы достижения этого?? Я обнаружил, что NSCoding подходит, но не могу понять, как его использовать. Любая помощь будет оценена.


person Shri    schedule 13.11.2011    source источник


Ответы (5)


В блоге Ray Wenderlich, который объясняет, как работать с NSCoding.

Просто вам просто нужно реализовать методы *-(void)encodeWithCoder:(NSCoder )encode и *-(id)initWithCoder:(NSCoder )decoder в вашем объекте, который уважает такой протокол NSCoding:

// Header
@interface AnObject: NSObject <NSCoding>

// Implementation
- (void) encodeWithCoder:(NSCoder *)encoder {
    [encoder encodeObject:ivar1 forKey:@"ivar1"];
    [encoder encodeFloat:ivar2 forKey:@"ivar2"];
}

- (id)initWithCoder:(NSCoder *)decoder {
    self = [super initWithCoder:coder];
    ivar1 = [[coder decodeObjectForKey:@"ivar1"] retain];
    ivar2 = [[coder decodeObjectForKey:@"ivar2"] retain];
    return self;
}

Вы также можете ознакомиться с официальной документацией здесь.

person Yannick Loriot    schedule 13.11.2011
comment
Я получаю утечку памяти в декодере (id)initWithCoder:(NSCoder *) в каждой строке. Вы знаете, что может быть возможной причиной? (Мои iVar1 и ivar2 являются строками) - person Shri; 19.11.2011
comment
Вы выпустили их в методе Dealloc? - person Yannick Loriot; 19.11.2011
comment
@YannickL.hello, я хочу спросить, стоят ли эти вещи с NSCoding того или лучше остановиться на NSDictionary, например, для кода, который вы написали выше? Есть какие-то критерии выбора или это просто дело вкуса? Если да, то как насчет стоимости ресурсов, что дешевле? Возможно, это тема для целого вопроса, но в теоретическом пространстве она тихая, поэтому я буду очень признателен, если вы ответите здесь или отредактируете свой вопрос. Спасибо. - person Dima Deplov; 18.02.2014
comment
@flinth Я думаю, что с точки зрения шаблона проектирования лучше использовать протокол NSCoding, чем NSDictionary. Использование NSDictionary подразумевает создание нового объекта, и у вас нет руки над сериализацией. Для проверки производительности вы можете проверить эту ссылку: developer.apple.com/library/mac/documentation/Cocoa/Conceptual/. - person Yannick Loriot; 19.02.2014

После реализации encodeWithCoder: и initWithCoder: для пользовательского класса вот как писать и читать:

Писать:

NSError  *error;
NSData* archiveData = [NSKeyedArchiver archivedDataWithRootObject:yourClassInstance];
[archiveData writeToFile:filePath options:NSDataWritingAtomic error:&error];

Читать:

NSData *archiveData = [NSData dataWithContentsOfFile:filePath];
YourClass *yourClassInstance = (NSArray*)[NSKeyedUnarchiver unarchiveObjectWithData:archiveData];
person zaph    schedule 13.11.2011
comment
Для удаления определенного объекта, как это сделать? - person raaz; 14.11.2011

Соответствие вашего класса XYZ Протокол NSCoding действительно очень подходит для того, что вы пытаетесь сделать. Для этого добавьте протокол NSCoding в объявление интерфейса вашего класса:

@interface XYZ <NSCoding>

Затем вам нужно реализовать encodeWithCoder: и initWithCoder: в вашей реализации. «Кодировать с помощью кодера» означает «взять текущее состояние вашего объекта и упаковать его в общий объект, называемый «кодировщиком»». «Инициализация с кодером» означает «данный уже упакованный объект кодировщика, сконфигурируйте себя с данными, упакованными в кодировщик». В обоих этих методах «кодер» будет экземпляром NSCoder, который имеет довольно простой интерфейс для кодирования ("упаковки") и декодирования ("распаковки") основных типов.

Вот пример реализации:

@implementation XYZ
- (id)initWithCoder:(NSCoder*)encoder
{
    self = [self init];

    if (self)
    {
        self.myIntProperty = [encoder decodeIntForKey:@"myInt"];
    }

    return self;
}

– (void)encodeWithCoder:(NSCoder*)decoder
{
    [decoder encodeInt:self.myIntProperty forKey:@"myInt"];
}

@end
person Josh Rosen    schedule 13.11.2011

Вам нужно реализовать два метода: -initWithCoder: и -encodeWithCoder:. Прочтите NSCoding документы; это довольно простые методы. В основном вы хотите кодировать/декодировать переменные экземпляра с помощью ключа, соответствующего его имени.

person Jeff Kelley    schedule 13.11.2011

Это довольно прямолинейно. Для данного класса Foo:

#import <Foundation/Foundation.h>

@interface Foo : NSObject <NSCoding> {
    NSArray* bar_;
    NSUInteger baz_;
}

@property (nonatomic, retain) NSArray *bar;
@property (nonatomic, assign) NSUInteger baz;

@end

Все, что вам нужно сделать, чтобы соответствовать протоколу NSCoding, — это реализовать сообщения initWithCoder: и decodeWithCoder:

#import "Foo.h"

@implementation Foo
@synthesize bar = bar_;
@synthesize baz = baz_;

#pragma mark - NSCoding

- (void)encodeWithCoder:(NSCoder *)encoder  {
    [encoder encodeObject:self.bar forKey:@"bar"];
    [encoder encodeInteger:self.baz forKey:@"baz"];
}

- (id)initWithCoder:(NSCoder *)decoder  {
    if ((self = [super init])) {
        self.bar = [decoder decodeObjectForKey:@"bar"];
        self.baz = [decoder decodeIntegerForKey:@"baz"];
    }
    return self;
}

- (void) dealloc {

    [bar_ release];
    [super dealloc];    
}

@end
person Wayne Hartman    schedule 13.11.2011