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

#iphone #objective-c #ios #cocoa-touch #nsdocumentdirectory

#iPhone #objective-c #iOS #cocoa-touch #nsdocumentdirectory

Вопрос:

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

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

Ответ №1:

В блоге Рэя Вендерлиха есть очень хороший учебник, в котором объясняется, как работать с 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;
}
  

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

Комментарии:

1. Я получаю утечку памяти в декодере — (id)initWithCoder: (NSCoder *) в каждой строке. Знаете ли вы, что может быть возможной причиной? (Мои iVar1 и ivar2 являются строками)

2. Вы освободили их с помощью метода dealloc?

3. @YannickL.hello, я хочу спросить, NSCoding стоит ли это того или лучше остановиться на NSDictionary коде, который вы написали выше, например? Есть ли какие-либо критерии для выбора или это просто дело вкуса? Если да, то как насчет затрат на ресурсы, что дешевле? Возможно, это тема для целого вопроса, но в теоретическом плане это тихо, поэтому я буду очень благодарен, если вы ответите здесь или отредактируете свой вопрос. Спасибо.

4. @flinth Я думаю, что лучше использовать NSCoding протокол, чем NSDictionary с точки зрения шаблона проектирования. Использование a NSDictionary подразумевает создание нового объекта, и у вас нет полномочий по сериализации. Для проверки производительности вы можете проверить эту ссылку: developer.apple.com/library/mac/documentation/Cocoa/Conceptual /… .

Ответ №2:

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

Записать:

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

Читать:

 NSData *archiveData = [NSData dataWithContentsOfFile:filePath];
YourClass *yourClassInstance = (NSArray*)[NSKeyedUnarchiver unarchiveObjectWithData:archiveData];
  

Комментарии:

1. Как это сделать для удаления определенного объекта?

Ответ №3:

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

 @interface XYZ <NSCoding>
  

Затем вам необходимо реализовать encodeWithCoder: и initWithCoder: в вашей реализации. «Кодировать с помощью coder» означает «взять текущее состояние вашего объекта и упаковать его в универсальный объект, называемый «encoder»». «Инициализация с помощью coder» означает «учитывая уже упакованный объект encoder, настройте себя с использованием данных, упакованных в encoder». В обоих этих методах ‘encoder’ будет экземпляром 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
  

Ответ №4:

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

Ответ №5:

Это довольно просто. Для данного класса 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